forzar example español como collector activar java garbage-collection

java - example - ¿Por qué no llama explícitamente a finalize() o inicia el recolector de basura?



garbage collector java example (8)

Después de leer esta pregunta , me acordé de cuando me enseñaron Java y me dijeron que nunca llamara a finalize () o que ejecutara el recolector de basura porque "es una gran caja negra de la que nunca tendrá que preocuparse". ¿Alguien puede hervir el razonamiento de esto hasta unas pocas oraciones? Estoy seguro de que podría leer un informe técnico de Sun sobre este tema, pero creo que una respuesta sencilla, breve y simple satisfaría mi curiosidad.


El GC hace muchas optimizaciones sobre cuándo finalizar correctamente las cosas.

Entonces, a menos que esté familiarizado con la forma en que el GC realmente funciona y cómo marca las generaciones, llamar manualmente para finalizar o iniciar GC''ing probablemente perjudicará el rendimiento más que la ayuda.


La respuesta corta: la recolección de basura de Java es una herramienta muy finamente ajustada. System.gc () es un martillo.

El montón de Java se divide en diferentes generaciones, cada una de las cuales se recopila utilizando una estrategia diferente. Si adjunta un generador de perfiles a una aplicación saludable, verá que rara vez tiene que ejecutar los tipos más caros de colecciones porque la mayoría de los objetos son capturados por el recopilador de copias más rápido de la generación joven.

Llamar a System.gc () directamente, aunque técnicamente no está garantizado para hacer nada, en la práctica desencadenará una costosa colección stop-the-world full heap. Esto es casi siempre lo incorrecto de hacer . Cree que está ahorrando recursos, pero en realidad los está desperdiciando sin una buena razón, forzando a Java a volver a verificar todos sus objetos en vivo "por las dudas".

Si tiene problemas con las pausas de GC durante momentos críticos, es mejor que configure la JVM para usar el recopilador de marca / barrido simultáneo, que fue diseñado específicamente para minimizar el tiempo que se pasa en pausa, en lugar de tratar de manejar el problema y solo rompiéndolo más.

El documento de Sun en el que estaba pensando está aquí: Java SE 6 HotSpot ™ Virtual Machine Recolección de basura Tuning

(Otra cosa que quizás no sepas: la implementación de un método finalize () en tu objeto hace que la recolección de basura sea más lenta. En primer lugar, tomará dos carreras de GC para recolectar el objeto: una para ejecutar finalize () y la siguiente para asegurar que el objeto no fue resucitó durante la finalización. En segundo lugar, los objetos con métodos finalize () tienen que ser tratados como casos especiales por el GC porque tienen que ser recolectados individualmente, no pueden ser desechados a granel.


No te molestes con los finalizadores.

Cambiar a la recolección de basura incremental.

Si desea ayudar al recolector de basura, anule las referencias a los objetos que ya no necesita. Menos camino a seguir = basura más explícita.

No olvide que las instancias de clase interna (no estáticas) mantienen referencias a su instancia de clase principal. Entonces, un hilo de clase interna mantiene mucho más equipaje de lo que cabría esperar.

En una línea muy relacionada, si está utilizando serialización y ha serializado objetos temporales, necesitará borrar los cachés de serialización, llamando a ObjectOutputStream.reset () o su proceso perderá memoria y finalmente morirá. Lo malo es que los objetos no transitorios se volverán a serializar. ¡La serialización de objetos de resultados temporales puede ser un poco más desordenada de lo que piensas!

Considera usar referencias suaves. Si no sabes qué son las referencias suaves, lee el javadoc para java.lang.ref.SoftReference

Manténgase alejado de referencias Phantom y referencias Débiles a menos que realmente se excita.

Finalmente, si realmente no puede tolerar el GC use Realtime Java.

No, no estoy bromeando.

La implementación de referencia es de descarga gratuita y el libro de Peter Dibbles de SUN es muy bueno para leer.


Suponiendo que los finalizadores son similares a su homónimo de .NET, entonces realmente solo necesita llamarlos cuando tenga recursos tales como identificadores de archivos que pueden tener fugas. La mayoría de las veces sus objetos no tienen estas referencias, por lo que no necesitan ser llamados.

Es malo intentar recoger la basura porque no es realmente tu basura. Le ha indicado a la máquina virtual que asigne un poco de memoria cuando creó objetos, y el recolector de basura está ocultando información sobre esos objetos. Internamente, el GC realiza optimizaciones en las asignaciones de memoria que realiza. Cuando intenta recolectar la basura manualmente, no tiene conocimiento de lo que la GC quiere retener y deshacerse de ella, simplemente está forzando su mano. Como resultado, se arruinan los cálculos internos.

Si supiera más acerca de lo que el GC contenía internamente, entonces podría tomar decisiones más informadas, pero luego habría perdido los beneficios de GC.


En cuanto a los finalizadores:

  1. Ellos son virtualmente inútiles. No están garantizados para ser llamados en el momento oportuno, o de hecho, en absoluto (si el GC nunca se ejecuta, tampoco lo harán los finalizadores). Esto significa que generalmente no deberías confiar en ellos.
  2. No se garantiza que los finalizadores sean idempotentes. El recolector de basura tiene mucho cuidado para garantizar que nunca invocará finalize() más de una vez en el mismo objeto. Con objetos bien escritos, no importará, pero con objetos mal escritos, la finalización de llamadas varias veces puede causar problemas (por ejemplo, la publicación doble de un recurso nativo ... bloqueo).
  3. Cada objeto que tiene un método finalize() también debe proporcionar un método close() (o similar). Esta es la función a la que deberías llamar. por ejemplo, FileInputStream.close() . No hay ninguna razón para llamar a finalize() cuando tienes un método más apropiado para que lo llames.

Evite los finalizadores. No hay garantía de que serán llamados de manera oportuna. Podría tomar bastante tiempo antes de que el sistema de administración de memoria (es decir, el recolector de basura) decida recoger un objeto con un finalizador.

Muchas personas usan finalizadores para hacer cosas como conexiones de socket cercanas o eliminar archivos temporales. De este modo, hace que el comportamiento de su aplicación sea impredecible y se vincule cuando la JVM vaya a GC su objeto. Esto puede conducir a escenarios "sin memoria", no debido a que el Heap de Java se está agotando, sino más bien debido a que el sistema se está quedando sin controladores para un recurso en particular.

Otra cosa a tener en cuenta es que la introducción de llamadas a System.gc () o tales martillos puede mostrar buenos resultados en su entorno, pero no necesariamente se traducirán a otros sistemas. No todos ejecutan la misma JVM, hay muchos, SUN, IBM J9, BEA JRockit, Harmony, OpenJDK, etc ... Esta JVM se ajusta a JCK (aquellos que oficialmente han sido probados), pero tienen una gran cantidad de libertad cuando se trata de hacer las cosas rápido. GC es una de esas áreas en las que todos invierten fuertemente. Usar un martillo muchas veces destruirá ese esfuerzo.


usted no llama al método de finalización en absoluto.


El problema real con el cierre de los controladores OS en finalizar es que los finales se ejecutan en un orden no garantizado. Pero si tienes controles para las cosas que bloquean (por ejemplo, los sockets), es posible que tu código entre en una situación de estancamiento (nada trivial).

Así que estoy para cerrar explícitamente los identificadores de una manera ordenada predecible. Básicamente, el código para tratar los recursos debe seguir el patrón:

SomeStream s = null; ... try{ s = openStream(); .... s.io(); ... } finally { if (s != null) { s.close(); s = null; } }

Se vuelve aún más complicado si escribe sus propias clases que funcionan a través de JNI y abren identificadores. Debe asegurarse de que las manijas estén cerradas (liberadas) y de que solo sucedan una vez. El manejo frecuente del SO en Desktop J2SE es Graphics[2D] . Incluso BufferedImage.getGrpahics() puede devolverle el identificador que apunta a un controlador de video (en realidad contiene el recurso en la GPU). Si no lo sueltas tú mismo y lo dejas como recolector de basura para hacer el trabajo, es posible que encuentres una extraña situación OutOfMemory y similares cuando te quedas sin mapas de bits asignados a la tarjeta de video pero aún tienes mucha memoria. En mi experiencia, sucede con bastante frecuencia en bucles estrechos trabajando con objetos gráficos (extracción de miniaturas, escalado, definición, nombre).

Básicamente GC no se ocupa de la responsabilidad de los programadores en la correcta gestión de los recursos. Solo se ocupa de la memoria y nada más. El Stream.finalize llamando a close () en mi humilde opinión se implementaría mejor lanzando una excepción nueva RuntimeError ("basura que recoge la secuencia que todavía está abierta"). Ahorrará horas y días de depuración y limpieza del código después de que los aficionados descuidados abandonaron los extremos.

Feliz codificación.

Paz.