¿Por qué implementarías alguna vez finalizar ()?

He estado leyendo muchas de las preguntas sobre Java novato en finalize () y encuentro algo desconcertante que nadie haya dejado claro que finalize () es una forma poco confiable de limpiar los recursos. Vi a alguien comentar que lo usan para limpiar Connections, lo que es realmente aterrador, ya que la única forma de acercarse lo suficiente a la garantía de que se cierra una conexión es implementar try (catch) finalmente.

No fui educado en CS, pero he estado progtwigndo en Java profesionalmente durante casi una década y nunca he visto a nadie implementar finalize () en un sistema de producción. Esto todavía no significa que no tiene sus usos, o que las personas con las que he trabajado lo han estado haciendo bien.

Entonces mi pregunta es, ¿qué casos de uso existen para implementar finalize () que no se pueden manejar de manera más confiable a través de otro proceso o syntax dentro del lenguaje?

Proporcione escenarios específicos o su experiencia, simplemente repetir un libro de texto de Java o finalizar el uso previsto no es suficiente, y no es la intención de esta pregunta.

Podría usarlo como protección para un objeto que contenga un recurso externo (zócalo, archivo, etc.). Implemente un método close() y documente que necesita ser llamado.

Implemente finalize() para hacer el proceso close() si detecta que no se ha realizado. Tal vez con algo arrojado a stderr para señalar que estás limpiando después de una llamada con errores.

Proporciona seguridad adicional en una situación excepcional / con errores. No todos los que llaman van a hacer las try {} finally {} correctas try {} finally {} cada vez. Desafortunado, pero cierto en la mayoría de los entornos.

Estoy de acuerdo en que rara vez es necesario. Y como lo comentan los comentaristas, viene con GC arriba. Úselo solo si necesita esa seguridad de “cinturón y tirantes” en una aplicación de larga duración.

Veo que a partir de Java 9, Object.finalize() está en desuso. Nos señalan a java.lang.ref.Cleaner y java.lang.ref.PhantomReference como alternativas.

finalize () es una pista a la JVM de que podría ser bueno ejecutar su código en un momento no especificado. Esto es bueno cuando quieres que el código misteriosamente no se ejecute.

Hacer algo significativo en los finalizadores (básicamente cualquier cosa, excepto el registro) también es bueno en tres situaciones:

  • desea apostar que otros objetos finalizados estarán en un estado que el rest de su progtwig considera válido.
  • Desea agregar muchos códigos de comprobación a todos los métodos de todas sus clases que tienen un finalizador, para asegurarse de que se comporten correctamente después de la finalización.
  • desea resucitar accidentalmente los objetos finalizados, y pasar mucho tiempo tratando de descubrir por qué no funcionan, y / o por qué no se finalizan cuando finalmente se liberan.

Si cree que necesita finalizar (), a veces lo que realmente quiere es una referencia fantasma (que en el ejemplo dado podría contener una referencia difícil a una conexión usada por su referencia, y cerrarla después de que la referencia fantasma haya sido puesta en cola). Esto también tiene la propiedad de que misteriosamente nunca se ejecuta, pero al menos no puede invocar métodos o resucitar objetos finalizados. Por lo tanto, es perfecto para las situaciones en las que no es necesario cerrar la conexión de forma limpia, pero sí lo desea, y los clientes de su clase no pueden o no se acercan (lo cual es bastante justo, ¿De qué sirve tener un recolector de basura si diseñas interfaces que requieren una acción específica antes de la recostackción? Eso simplemente nos remonta a los tiempos de malloc / free.)

Otras veces necesita el recurso que cree que está logrando para ser más robusto. Por ejemplo, ¿por qué necesitas cerrar esa conexión? En última instancia, debe basarse en algún tipo de E / S proporcionado por el sistema (socket, archivo, lo que sea), entonces ¿por qué no puede confiar en que el sistema lo cerrará cuando el nivel más bajo de recursos esté disponible? Si el servidor en el otro extremo requiere absolutamente que cierre la conexión de forma limpia en lugar de simplemente dejar caer el socket, ¿qué sucederá cuando alguien se desconecte del cable de alimentación de la máquina en la que se está ejecutando su código o se apague la red que interviene?

Descargo de responsabilidad: he trabajado en una implementación de JVM en el pasado. Odio los finalizadores.

Una regla simple: nunca use finalizadores. El solo hecho de que un objeto tenga un finalizador (independientemente del código que ejecuta) es suficiente para causar una sobrecarga considerable para la recolección de basura.

De un artículo de Brian Goetz:

Los objetos con finalizadores (aquellos que tienen un método de finalización () no trivial) tienen una sobrecarga significativa en comparación con los objetos sin finalizadores, y deben usarse con moderación. Los objetos finalizables son más lentos de asignar y más lentos de recostackr. En el momento de la asignación, la JVM debe registrar cualquier objeto finalizable con el recolector de elementos no utilizados y (al menos en la implementación HotSpot JVM) los objetos finalizables deben seguir una ruta de asignación más lenta que la mayoría de los demás objetos. Del mismo modo, los objetos finalizables son más lentos para recostackr también. Se necesitan al menos dos ciclos de recolección de elementos no utilizados (en el mejor de los casos) antes de que se pueda recuperar un objeto finalizable, y el recolector de elementos no utilizados debe realizar un trabajo adicional para invocar el finalizador. El resultado es más tiempo dedicado a asignar y recostackr objetos y más presión sobre el recolector de elementos no utilizados, ya que la memoria utilizada por objetos finalizables inalcanzables se conserva por más tiempo. Combine eso con el hecho de que no está garantizado que los finalizadores se ejecuten en un marco de tiempo predecible, o incluso en absoluto, y puede ver que hay relativamente pocas situaciones para las cuales la finalización es la herramienta correcta para usar.

La única vez que utilicé la finalización en el código de producción fue implementar una verificación de que los recursos de un objeto determinado se habían limpiado, y si no, registrar un mensaje muy vocal. En realidad, no intentó hacerlo solo, simplemente gritó mucho si no se hizo correctamente. Resultó ser bastante útil.

He estado haciendo Java profesionalmente desde 1998, y nunca he implementado finalize (). Ni una sola vez.

No estoy seguro de lo que puedes hacer con esto, pero …

 itsadok@laptop ~/jdk1.6.0_02/src/ $ find . -name "*.java" | xargs grep "void finalize()" | wc -l 41 

Así que supongo que el Sol encontró algunos casos en los que (creen) deberían usarse.

Usé finalize una vez para entender qué objetos se estaban liberando. Puedes jugar algunos buenos juegos con estática, recuento de referencias y demás, pero fue solo para el análisis.

La respuesta aceptada es buena, solo quería agregar que ahora hay una manera de tener la funcionalidad de finalizar sin usarla en absoluto.

Mira las clases de “Referencia”. Referencia débil, etc.

Puede usarlos para mantener una referencia a todos sus objetos, pero esta referencia ALONE no detendrá GC. Lo bueno de esto es que puede hacer que llame a un método cuando se eliminará, y se puede garantizar que este método se invoque.

Otra cosa a tener en cuenta. Cada vez que ve algo como esto en cualquier parte del código (no solo para finalizar, sino que es donde es más probable que lo vea):

 public void finalize() { ref1 = null; ref2 = null; othercrap = null; } 

Es una señal de que alguien no sabía lo que estaban haciendo. “Limpiar” de esta manera prácticamente nunca se necesita. Cuando la clase es GC’d, esto se hace automáticamente.

Si encuentras un código como este en una finalización, se garantiza que la persona que lo escribió estaba confundida.

Si está en otro lugar, podría ser que el código sea un parche válido para un modelo incorrecto (una clase permanece por mucho tiempo y por alguna razón las cosas a las que hacía referencia debían liberarse manualmente antes de que el objeto sea GC). Generalmente se debe a que alguien olvidó eliminar a un oyente o algo y no puede entender por qué su objeto no está siendo sometido a GC, por lo que simplemente borran las cosas a las que se refiere y se encogen de hombros y se alejan.

Nunca debe usarse para limpiar cosas “Más rápido”.

 class MyObject { Test main; public MyObject(Test t) { main = t; } protected void finalize() { main.ref = this; // let instance become reachable again System.out.println("This is finalize"); //test finalize run only once } } class Test { MyObject ref; public static void main(String[] args) { Test test = new Test(); test.ref = new MyObject(test); test.ref = null; //MyObject become unreachable,finalize will be invoked System.gc(); if (test.ref != null) System.out.println("MyObject still alive!"); } } 

=================================

resultado:

Esto es finalizado

MyObject sigue vivo!

==================================

Por lo tanto, puede hacer que una instancia inalcanzable sea accesible en el método de finalización.

finalizar puede ser útil para detectar fugas de recursos. Si el recurso debe cerrarse pero no está escrito, el hecho de que no estaba cerrado a un archivo de registro y lo cierra. De esta forma, elimina la fuga de recursos y se da a sí mismo una manera de saber que ha sucedido para que pueda solucionarlo.

He estado progtwigndo en Java desde 1.0 alpha 3 (1995) y aún tengo que anular la finalización de nada …

No debe depender de finalize () para limpiar sus recursos por usted. finalize () no se ejecutará hasta que la clase sea basura, si es así. Es mucho mejor liberar recursos explícitamente cuando hayas terminado de usarlos.

Para resaltar un punto en las respuestas anteriores: los finalizadores se ejecutarán en el único hilo del GC. He oído hablar de una gran demostración de Sun donde los desarrolladores agregaron un pequeño sueño a algunos finalizadores e intencionalmente trajeron una demostración en 3D que de otra manera se pondría de rodillas.

Es mejor evitarlo, con la posible excepción de los diagnósticos de prueba-env.

El pensamiento de Eckel en Java tiene una buena sección sobre esto.

Al escribir código que otros desarrolladores utilizarán, se requiere un método de “limpieza” para liberar recursos. A veces esos otros desarrolladores se olvidan de llamar a su método de limpieza (o cerrar, o destruir, o lo que sea). Para evitar posibles pérdidas de recursos, puede verificar el método de finalización para asegurarse de que se haya llamado al método y, de lo contrario, puede llamarlo usted mismo.

Muchos controladores de base de datos hacen esto en sus implementaciones de Declaración y Conexión para proporcionar un poco de seguridad contra los desarrolladores que se olvidan de llamarlos de cerca.

Hmmm, una vez lo usé para limpiar objetos que no se devolvían a un grupo existente. Se pasaron mucho, por lo que era imposible saber cuándo podrían devolverse a la piscina con seguridad. El problema fue que introdujo una gran penalización durante la recolección de basura que fue mucho mayor que cualquier ahorro al agrupar los objetos. Estuvo en producción durante aproximadamente un mes antes de que arrancara todo el conjunto, hiciera que todo fuera dynamic y terminé con eso.

Editar: Bien, realmente no funciona. Lo implementé y pensé que si falla, a veces eso está bien para mí, pero ni siquiera llamó al método de finalización una sola vez.

No soy un progtwigdor profesional pero en mi progtwig tengo un caso que creo que es un ejemplo de un buen caso de uso de finalize (), que es un caché que escribe su contenido en el disco antes de que se destruya. Debido a que no es necesario que se ejecute cada vez que se destruye, solo acelera mi progtwig, espero que no lo haya hecho mal.

 @Override public void finalize() { try {saveCache();} catch (Exception e) {e.printStackTrace();} } public void saveCache() throws FileNotFoundException, IOException { ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("temp/cache.tmp")); out.writeObject(cache); } 

Puede ser útil para eliminar cosas que se han agregado a un lugar global / estático (por necesidad) y deben eliminarse cuando se elimina el objeto. Por ejemplo:

     vacío privado addGlobalClickListener () {
         weakAwtEventListener = new WeakAWTEventListener (esto);

         Toolkit.getDefaultToolkit (). AddAWTEventListener (weakAwtEventListener, AWTEvent.MOUSE_EVENT_MASK);
     }

     @Anular
     protected void finalize () throws Throwable {
         super.finalize ();

         if (weakAwtEventListener! = null) {
             Toolkit.getDefaultToolkit (). RemoveAWTEventListener (weakAwtEventListener);
         }
     }

Tenga cuidado con lo que hace en una finalización (). Especialmente si lo está usando para cosas como llamar a close () para asegurar que los recursos se limpien. Nos encontramos con varias situaciones en las que teníamos bibliotecas JNI vinculadas al código Java en ejecución, y en cualquier circunstancia en la que utilizáramos finalize () para invocar métodos JNI, obtendríamos muy mala corrupción del montón de Java. La corrupción no fue causada por el código JNI subyacente en sí, todos los rastros de memoria estaban bien en las bibliotecas nativas. Era solo el hecho de que estábamos llamando métodos JNI desde el finalize () en absoluto.

Esto fue con un JDK 1.5 que todavía está en uso generalizado.

No descubrimos que algo salió mal hasta mucho más tarde, pero al final el culpable siempre fue el método de finalización () haciendo uso de llamadas JNI.

iirc: puede usar el método de finalización como medio para implementar un mecanismo de puesta en común para recursos costosos, por lo que no obtienen los GC también.

La respuesta aceptada enumera que se puede cerrar un recurso durante la finalización.

Sin embargo, esta respuesta muestra que, al menos en java8 con el comstackdor JIT, se encuentra con problemas inesperados en los que a veces se llama al finalizador incluso antes de que termine de leer una secuencia mantenida por su objeto.

Por lo tanto, incluso en esa situación, la convocatoria de finalización no sería recomendable.

Personalmente, casi nunca utilizo finalize() excepto en una circunstancia rara: hice una colección personalizada de tipo genérico y escribí un método personalizado finalize() que hace lo siguiente:

 public void finalize() throws Throwable { super.finalize(); if (destructiveFinalize) { T item; for (int i = 0, l = length(); i < l; i++) { item = get(i); if (item == null) { continue; } if (item instanceof Window) { ((Window) get(i)).dispose(); } if (item instanceof CompleteObject) { ((CompleteObject) get(i)).finalize(); } set(i, null); } } } 

( CompleteObject es una interfaz que hice que le permite especificar que ha implementado métodos de Object raramente implementados como #finalize() , #hashCode() y #clone() )

Entonces, usando un #setDestructivelyFinalizes(boolean) hermano #setDestructivelyFinalizes(boolean) , el progtwig que usa mi colección puede (ayudar) a garantizar que la destrucción de una referencia a esta colección también destruye las referencias a su contenido y descarta cualquier ventana que pueda mantener la JVM activa involuntariamente. Consideré también detener cualquier hilo, pero eso abrió una nueva lata de gusanos.

Los recursos (Archivo, Socket, Stream, etc.) deben cerrarse una vez que hayamos terminado con su uso. Por lo general, tienen el método close() que generalmente llamamos en la sección close() try-catch declaraciones try-catch . Algunas veces, finalize() también puede ser utilizado por algunos desarrolladores, pero IMO no es una forma adecuada, ya que no hay garantía de que se llame siempre a finalize.

En Java 7 tenemos la statement try-with-resources que se puede usar como:

 try (BufferedReader br = new BufferedReader(new FileReader(path))) { // Processing and other logic here. } catch (Exception e) { // log exception } finally { // Just in case we need to do some stuff here. } 

En el ejemplo anterior, try-with-resource cerrará automáticamente el recurso BufferedReader invocando el método close() . Si queremos también podemos implementar Closeable en nuestras propias clases y usarlo de manera similar. OMI parece más simple y simple de entender.

Como nota al margen:

Un objeto que anula finalize () es tratado especialmente por el recolector de basura. Por lo general, un objeto se destruye inmediatamente durante el ciclo de recolección una vez que el objeto ya no está dentro del scope. Sin embargo, los objetos finalizables se mueven a una cola, donde los hilos de finalización separados agotan la cola y ejecutan el método finalize () en cada objeto. Una vez que finaliza el método finalize (), el objeto estará finalmente listo para la recolección de basura en el siguiente ciclo.

finalizar desaprobado en java-9