java performance memory-management finalizer object-destruction

java - ¿Por qué los finalizadores tienen una "penalización severa de rendimiento"?



performance memory-management (6)

Efectivo Java dice:

Hay una penalización severa en el rendimiento por usar finalizadores.

¿Por qué es más lento destruir un objeto usando los finalizadores?


Habiendo encontrado realmente uno de esos problemas:

En Sun HotSpot JVM, los finalizadores se procesan en un subproceso que tiene una prioridad baja y fija. En una aplicación de alta carga, es fácil crear objetos necesarios para la finalización más rápido que el subproceso de finalización de baja prioridad puede procesarlos. Mientras tanto, el espacio en el montón usado por los objetos pendientes de finalización no está disponible para otros usos. Eventualmente, su aplicación puede pasar todo el tiempo recogiendo basura, porque toda la memoria disponible está en uso por los objetos pendientes de finalización.

Esto es, por supuesto, además de las otras muchas razones para no usar finalizadores que se describen en Java efectivo.


Mi pensamiento es este: Java es un lenguaje recogido de basura, que desasigna la memoria en función de sus propios algoritmos internos. De vez en cuando, el GC escanea el montón, determina a qué objetos ya no se hace referencia y desasigna la memoria. Un finalizador interrumpe esto y fuerza la desasignación de la memoria fuera del ciclo del GC, lo que puede causar ineficiencias. Creo que las mejores prácticas son usar finalizadores solo cuando sea ABSOLUTAMENTE necesario, como liberar manejadores de archivos o cerrar conexiones de DB, lo que debería hacerse de manera determinista.


Por la forma en que funciona el recolector de basura. Para el rendimiento, la mayoría de los GC de Java utilizan un colector de copiado, donde los objetos efímeros se asignan a un bloque de memoria "eden", y cuando llega el momento de recopilar esa generación de objetos, el GC solo necesita copiar los objetos que todavía están "vivos" para un espacio de almacenamiento más permanente, y luego puede limpiar (liberar) todo el bloque de memoria "eden" a la vez. Esto es eficiente porque la mayoría del código Java creará muchos miles de instancias de objetos (primitivas en recuadro, matrices temporales, etc.) con tiempos de vida de solo unos pocos segundos.

Sin embargo, cuando tienes finalizadores en la mezcla, el GC no puede simplemente borrar toda una generación a la vez. En su lugar, necesita descifrar todos los objetos de esa generación que deben completarse y ponerlos en cola en un hilo que realmente ejecuta los finalizadores. Mientras tanto, el GC no puede terminar de limpiar los objetos de manera eficiente. Por lo tanto, tiene que mantenerlos vivos más de lo que deberían, o tiene que retrasar la recolección de otros objetos, o ambos. Además, tiene el tiempo de espera arbitrario de ejecutar realmente los finalizadores.

Todos estos factores se suman a una penalización de tiempo de ejecución significativa, por lo que generalmente se prefiere la finalización determinística (utilizando un método close() o similar para finalizar explícitamente el estado del objeto).


Recogí mi copia Effective Java de mi escritorio para ver a qué se refería.

Si lees el Capítulo 2, Sección 6, entra en detalles sobre los diversos éxitos de rendimiento.

You can''t know when the finalizer will run, or even if it will at all. Because those resources may never be claimed, you will have to run with fewer resources.

Recomiendo leer la totalidad de la sección, explica las cosas mucho mejor de lo que puedo repetir aquí.


Si lee de cerca la documentación de finalize() , notará que los finalizadores permiten que un objeto evite que el GC lo recopile.

Si no hay un finalizador presente, el objeto simplemente puede eliminarse y no necesita más atención. Pero si hay un finalizador, debe verificarse luego, si el objeto no se volvió "visible" nuevamente.

Sin saber exactamente cómo se implementa la recolección de basura actual de Java (en realidad, debido a que hay diferentes implementaciones de Java, también hay diferentes GC), puede suponer que el GC tiene que hacer un trabajo adicional si un objeto tiene un finalizador, porque de esta característica.


Una razón por la que puedo pensar es que la limpieza explícita de la memoria no es necesaria si sus recursos son todos los objetos de Java y no el código nativo.