24

Effective Java dice:¿Por qué los finalizadores tienen una "penalización severa de rendimiento"?

Hay una penalización de rendimiento grave para el uso de los finalizadores.

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

+1

Puede que le guste este artículo, que explica cómo los finalizadores pueden hacer que los objetos sean alcanzables nuevamente, etc. También muestra por qué la composición puede salvar el día (en lugar de la herencia de implementación) en algunos casos: http: //java.sun. com/developer/technicalArticles/javase/finalization/ – SyntaxT3rr0r

Respuesta

22

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 tiene finalizadores en la mezcla, el GC no puede simplemente limpiar 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 importante penalización de tiempo de ejecución, 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).

+1

Esto, por supuesto, se centra en problemas con un coleccionista generacional. Otras estrategias de GC tienen diferentes problemas. Pero todos se reducen al GC que necesita realizar un trabajo extra, que incluye al menos dos pases a través de un objeto para liberarlo; uno para agregarlo a la cola de finalización, y otro para liberarlo realmente después de la finalización. –

+0

¿Tendría razón al pensar que varias clases de Java API comúnmente usadas tienen finalizadores para liberar recursos de O/S? Estoy pensando en 'FileOutputStream'. Por lo tanto, es poco probable que los finalizadores de algunos objetos retrasen el GC de los objetos que no usan finalizadores, porque la mayoría de los programas se verían afectados. – Raedwald

+1

@Raedwald: Correcto. Por ejemplo, la implementación OpenJDK de 'FileOutputStream' tiene un finalizador, que se puede ver mirando la fuente OpenJDK. (No puedo encontrar nada * que * requiera * la implementación estándar de la biblioteca para usar finalizadores). Así que, en la práctica, los objetos que son elegibles para GC pero que aún están pendientes de finalización son promovidos a la siguiente generación anterior (espacio de superviviente o ocupado) mientras el finalizador está en cola para ejecutarse. Pero la memoria real no se recuperará hasta la próxima vez que se obtenga la siguiente generación anterior. –

1

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 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.

+1

¿Realmente lo fuerza o simplemente sugiere? – corsiKa

+0

Principalmente a la derecha, pero los finalizadores no causan la desasignación de memoria fuera de un ciclo de GC. En cambio, si el GC determina que un objeto necesita estar finalizado, lo "resucita" y evita que el objeto se recopile hasta que se haya ejecutado el finalizador. Pero puede tomar un tiempo, ya que los finalizadores (IIRC) no se ejecutan hasta la próxima vez que se recolecte la generación titular. –

+1

"Creo que las mejores prácticas son utilizar finalizadores solo cuando sea ABSOLUTAMENTE necesario, como liberar manejadores de archivos o cerrar conexiones de BD": Tenga en cuenta que esto es precisamente donde los finalizadores * no * son apropiados, porque el finalizador puede ejecutarse arbitrariamente tarde o no llegar. – sleske

0

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.

1

Acabo de recoger mi copia Effective Java de mi escritorio para ver a qué se refiere.

Si lee 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.

recomendaría la lectura de la totalidad de la sección - que explica las cosas mucho mejor de lo que puedo loro aquí.

1

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

Si no hay un finalizador presente, el objeto simplemente se puede quitar 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, porque 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, debido a esta característica.

9

Tener realmente ejecutar en uno de esos problemas:

En el Sun HotSpot JVM, finalizadores se procesan en un hilo que se da a, baja prioridad 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 los finalizadores que se describen en Java efectivo.

Cuestiones relacionadas