Después de leer this question, 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 agradable, breve y simple satisfaría mi curiosidad.¿Por qué no llama explícitamente a finalize() o inicia el recolector de elementos no utilizados?
Respuesta
La respuesta corta: la recolección de basura de Java es una herramienta muy bien 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 se garantiza que haga nada, en la práctica desencadenará una costosa colección stop-the-world full heap. Esto es casi siempre es lo incorrecto de hacer. Cree que está ahorrando recursos, pero en realidad los está desperdiciando sin una buena razón, obligando 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 configurar la JVM para usar el recopilador de marca/barrido simultáneo, que fue diseñado específicamente para minimizar el tiempo pasado en pausa, en lugar de intentar tomar una almádena el problema y simplemente romperlo más.
El documento Sun estabas pensando en está aquí: Java SE 6 HotSpot™ Virtual Machine Garbage Collection Tuning
(Otra cosa que puede no saber: la aplicación de un método finalize() del objeto hace que la recolección de basura más lenta En primer lugar, se llevará a dos GC. se ejecuta para recoger el objeto: uno para ejecutar finalize() y el siguiente para asegurarse de que el objeto no 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 recogidos individualmente, no pueden simplemente tirarse a granel.)
El GC hace una gran cantidad de optimización sobre cuándo finalizar correctamente las cosas.
De modo que, a menos que esté familiarizado con la forma en que funciona el GC y cómo marca generaciones, llamar manualmente para finalizar o iniciar GC'ing probablemente perjudicará el rendimiento más que la ayuda.
Suponiendo que los finalizadores son similares a sus homónimos .NET, entonces solo necesita llamarlos cuando tenga recursos 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.
No se moleste 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 primaria. Entonces, un hilo de clase interna mantiene mucho más equipaje de lo que cabría esperar.
En un aspecto muy relacionado, si está utilizando la 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 eventualmente 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!
Considere el uso de referencias suaves. Si no sabe qué son las referencias suaves, lea javadoc para java.lang.ref.SoftReference
Manténgase alejado de las referencias Phantom y las 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.
Evite los finalizadores. No hay garantía de que serán llamados de manera oportuna. Podría pasar bastante tiempo antes de que el sistema de gestión de memoria (es decir, el recolector de basura) decida recoger un objeto con un finalizador.
Muchas personas usan los finalizadores para hacer cosas como cerrar conexiones de socket 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 las 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.
En cuanto a los finalizadores ir:
- Ellos son prácticamente 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.
- No se garantiza que los finalizadores sean idempotentes. El recolector de basura tiene mucho cuidado de garantizar que nunca llame al
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, el lanzamiento doble de un recurso nativo ... bloqueo). - Cada objeto que tiene un método
finalize()
también debe proporcionar un métodoclose()
(o similar). Esta es la función a la que deberías llamar. por ejemplo,FileInputStream.close()
. No hay ninguna razón para llamar alfinalize()
cuando tiene un método más apropiado que es para que lo llame.
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 tiene controles para las cosas que bloquean (piensen, por ejemplo, sockets), es posible que su código pueda entrar en una situación de estancamiento (nada trivial).
Así que estoy para cerrar explícitamente los identificadores de una manera ordenada predecible. Básicamente código para hacer frente a los recursos deben seguir el patrón:
SomeStream s = null;
...
try{
s = openStream();
....
s.io();
...
} finally {
if (s != null) {
s.close();
s = null;
}
}
Se hace aún más complicado si usted escribe sus propias clases que funcionan a través de JNI y los identificadores abiertos. Debe asegurarse de que las manijas estén cerradas (liberadas) y de que solo sucedan una vez. El control del sistema operativo con frecuencia pasado por alto 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.
Happy coding.
Paz.
- 1. Recolector de elementos no utilizados .NET y memoria virtual x64
- 2. ¿Por qué no se llama a Application.OnStartup?
- 3. ¿Ejecutar el recolector de elementos no utilizados desde la línea de comandos?
- 4. ¿El recolector de basura llama a Dispose()?
- 5. ¿Por qué Ada no tiene un recolector de basura?
- 6. ¿Qué objetos no limpia el recolector de basura?
- 7. qué método Finalize no pueden anular
- 8. ¿Qué sucede con los elementos DOM no utilizados?
- 9. ¿Por qué no se llama a mi delegado de CLLocationmanager?
- 10. ¿Por qué una instancia de UIWebView no llama a scrollViewDidScroll?
- 11. eliminando elementos DOM no utilizados para el rendimiento
- 12. ¿por qué textFieldDidEndEditing: no se llama?
- 13. Algoritmo de recolección de elementos no utilizados de JVM
- 14. iPhone: ¿por qué no se llama a drawRect?
- 15. aclaración de la recolección de elementos no utilizados de PHP
- 16. ¿Por qué se llama onResume() cuando se inicia una actividad?
- 17. ¿Por qué __init__ no se llama después __new__ A VECES
- 18. ¿Por qué C++ std :: list :: clear() no llama a destructores?
- 19. Excepción en el método finalize
- 20. Mapa de bits, Bitmap.recycle(), WeakReferences y recolección de elementos no utilizados
- 21. ¿Por qué se llama al destructor cuando el recolector de basura CPython está deshabilitado?
- 22. ¿Por qué estoy obligado a hacer referencia a los tipos en los constructores no utilizados?
- 23. ¿En qué situación se llama a Application_EndRequest pero no se llama a Application_BeginRequest?
- 24. JSON: elementos clave por identificación o no?
- 25. ¿Qué pasó internamente (JVM) cuando se llama al método System.gc() o finalize()?
- 26. ¿Por qué no se inicia mongrel en Rails 3.2.rc?
- 27. fbDidLogin no se llama
- 28. ¿Por qué no puedo pasar explícitamente el argumento tipo a un método Java genérico?
- 29. Buscar css no utilizados
- 30. ¿Por qué no se llama mi manejador de señal?
Consideraría el caso de uso de 'WeakReference' mucho más fuerte que el de' SoftReference'. Si 'Foo' necesita una referencia a' Bar' para beneficio de Foo, debe usar una referencia fuerte. Si 'Foo' necesita una referencia a' Bar' para beneficio de Bar, debería usar una referencia débil. Por ejemplo, 'Bar' puede querer ser notificado cada vez que' Foo' hace algo, pero 'Foo' sería igual de feliz si no tuviera que notificar a nadie. Si las únicas referencias a 'Bar' están en manos de cosas que realmente no les importa si existen, entonces no deberían existir. – supercat