¿Por qué implementarías finalize()?


He estado leyendo a través de muchas de las preguntas de Java novato en finalize() y me parece un poco desconcertante que nadie realmente ha dejado claro que finalize() es una forma poco fiable de limpiar los recursos. Vi a alguien comentar que lo usan para limpiar Conexiones, lo cual es realmente aterrador ya que la única manera de acercarse a una garantía de que una conexión está cerrada es implementar try (catch) finalmente.

No fui educado en CS, pero he estado programando en Java profesionalmente desde hace 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 tenga sus usos, o que las personas con las que he trabajado lo hayan estado haciendo bien.

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

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

 330
Author: Spencer Kormos, 2008-10-01

21 answers

Se podría utilizar como un backstop para un objeto que contiene un recurso externo (socket, archivo, etc.). Implementar un método close() y el documento que necesita ser llamado.

Implementa finalize() para hacer el procesamiento close() si detectas que no se ha hecho. Tal vez con algo arrojado a Stderr para señalar que estás limpiando después de una llamada de buggy.

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

Estoy de acuerdo en que rara vez se necesita. Y como comentaristas señalan, viene con gastos generales de GC. Solo use 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! Ellos nos señalan java.lang.ref.Cleaner y java.lang.ref.PhantomReference como alternativas.

 204
Author: John M,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-11 22:40:42

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

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

  • usted quiere apostar que otros objetos finalizados todavía estarán en un estado que el resto de su programa considere válido.
  • desea agregar un montón de código de comprobación a todos los métodos de todas sus clases que tienen un finalizador, para asegurarse de que se comportan correctamente después de la finalización.
  • desea resucitar accidentalmente objetos finalizados, y pasar mucho tiempo tratando de averiguar por qué no funcionan, y/o por qué no se finalizan cuando finalmente se liberan.

Si crees que necesitas finalize(), a veces lo que realmente quieres es una referencia fantasma (que en el ejemplo dado podría contener una referencia dura a una conexión utilizada por su referand, y ciérralo después de que la referencia phantom haya sido puesta en cola). Esto también tiene la propiedad de que puede misteriosamente nunca ejecutarse, pero al menos no puede llamar a métodos o resucitar objetos finalizados. Por lo tanto, es adecuado para situaciones en las que no es absolutamente necesario cerrar esa conexión de forma limpia, pero le gustaría hacerlo, y los clientes de su clase no pueden o no llamarán cerrar ellos mismos (lo que en realidad es justo - ¿cuál es el punto de tener un recolector de basura en absoluto si diseña interfaces que exigir que se tome una acción específica antes de la recolección? Eso nos devuelve a los días de malloc / free.)

Otras veces necesitas que el recurso que crees que estás logrando sea más robusto. Por ejemplo, ¿por qué necesitas cerrar esa conexión? En última instancia, debe basarse en algún tipo de E/S proporcionada por el sistema (socket, archivo, lo que sea), así que ¿por qué no puede confiar en el sistema para cerrarlo cuando el nivel más bajo de recurso es gced? Si el servidor en el otro extremo absolutamente requiere que cierre la conexión limpiamente en lugar de simplemente soltar el zócalo, entonces ¿qué va a suceder cuando alguien se tropieza con el cable de alimentación de la máquina en la que se está ejecutando el código, o la red que interviene se apaga?

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

 154
Author: Steve Jessop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-24 16:59:05

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

De un artículo de Brian Goetz:

Objetos con finalizadores (aquellos que tener un método no trivial finalize() ) tener gastos generales significativos en comparación con objetos sin finalizadores, y debería ser usado con moderación. Finalizable los objetos son más lentos de asignar y más lento para recoger. En la asignación tiempo, la JVM debe registrar cualquier objetos finalizables con la basura colector, y (al menos en el Implementación de Hotspot JVM) los objetos finalizables deben seguir una ruta de asignación más lenta que la mayoría de los otros objeto. Del mismo modo, finalizable los objetos también son más lentos de recolectar. Se toma al menos dos recolección de basura ciclos (en el mejor de los casos) antes de objeto finalizable puede ser reclamado, y el recolector de basura tiene que hacer trabajo extra para invocar el finalizador. El resultado es más tiempo dedicado asignación y recogida de objetos y más presión sobre la basura colector, porque la memoria utilizada por objetos finalizables inalcanzables es retenido más tiempo. Combine eso con el el hecho de que los finalizadores no garantizado para funcionar en cualquier predecible plazo, o incluso en absoluto, y usted puede ver que hay relativamente pocos situaciones para las que la finalización es la herramienta correcta para usar.

 50
Author: Tom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-01 17:38:32

La única vez que he utilizado finalize en el código de producción fue para implementar una verificación de que los recursos de un objeto dado se habían limpiado, y si no, entonces registrar un mensaje muy vocal. En realidad no trató de hacerlo por sí mismo, solo gritó mucho si no se hacía correctamente. Resultó ser bastante útil.

 43
Author: skaffman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-01 15:44:18

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

 32
Author: Paul Tomblin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-01 15:24:23

No estoy seguro de lo que puede hacer de 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 donde (ellos piensan) debería ser usado.

 24
Author: itsadok,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-05 14:42:56

Utilicé finalize una vez para entender qué objetos estaban siendo liberados. Puedes jugar algunos juegos con estática, conteo de referencias y cosas así but pero era solo para el análisis.

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

Mira las clases "Reference". Referencia débil, etc.

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

Otra cosa a tener en cuenta. Cada vez que vea algo como esto en cualquier lugar del código (no solo en finalize, 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 estaba haciendo. "Limpiar" como esto prácticamente nunca es necesario. Cuando la clase es GC'd, esto está hecho automática.

Si encuentras código como ese en un finalize, está garantizado que la persona que lo escribió estaba confundida.

Si está en otro lugar, podría ser que el código es un parche válido para un modelo malo (una clase permanece por mucho tiempo y por alguna razón las cosas a las que hace referencia tuvieron que ser liberadas manualmente antes de que el objeto sea GC'd). En general, es porque alguien olvidó eliminar un oyente o algo y no puede averiguar por qué su objeto no está siendo GC'd, por lo que simplemente lo elimina cosas a las que se refiere y encogerse de hombros y alejarse.

Nunca se debe usar para limpiar las cosas "más rápido".

 24
Author: Bill K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-16 16:22:57
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 finalizar

MyObject still alive!

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

Así que puede hacer que una instancia inalcanzable sea accesible en el método finalize.

 21
Author: Kiki,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-13 20:43:42

Finalizar puede ser útil para detectar fugas de recursos. Si el recurso debe ser cerrado pero no es escribir el hecho de que no fue cerrado a un archivo de registro y cerrarlo. De esa manera se elimina la fuga de recursos y se da una manera de saber que ha sucedido para que pueda solucionarlo.

He estado programando en Java desde 1.0 alpha 3 (1995) y todavía tengo que anular finalize para nada...

 8
Author: TofuBeer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-13 18:09:38

No deberías depender de finalize() para limpiar tus recursos. finalize () no se ejecutará hasta que la clase sea recogida como basura, si es así. Es mucho mejor liberar recursos explícitamente cuando haya terminado de usarlos.

 6
Author: Bill the Lizard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-01 15:27:49

Para resaltar un punto en las respuestas anteriores: los finalizadores se ejecutarán en el subproceso GC solitario. 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 de otra manera elegante a sus rodillas.

Es mejor evitar, con la posible excepción de test-env diagnostics.

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

 5
Author: Michael Easter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-01 17:43:12

Al escribir código que será utilizado por otros desarrolladores que requiere algún tipo de método de "limpieza" para ser llamado 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 fugas de recursos, puede verificar el método finalizar para asegurarse de que se llamó al método y, si no lo fue, puede llamarlo usted mismo.

Muchos controladores de base de datos hacen esto en su Declaración e implementaciones de conexión para proporcionar un poco de seguridad contra los desarrolladores que se olvidan de llamar cerca de ellos.

 2
Author: John Meagher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-01 15:25:07

Hmmm, una vez lo usé para limpiar objetos que no se estaban devolviendo a un grupo existente. Se pasaban mucho, por lo que era imposible saber cuándo podrían ser devueltos a la piscina de forma segura. El problema era que introducía una penalización enorme durante la recolección de basura que era mucho mayor que cualquier ahorro de agrupar los objetos. Estuvo en producción durante aproximadamente un mes antes de que arrancara toda la piscina, hiciera todo dinámico y terminara con ello.

 2
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-01 16:15:39

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

No soy un programador profesional, pero en mi programa tengo un caso que creo que es un ejemplo de un buen caso de usar finalize (), es decir, una caché que escribe su contenido en el disco antes de que se destruya. Porque no es necesario que se ejecute cada vez en la destrucción, solo acelera mi programa, yo 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);
}
 2
Author: user265243,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-03 12:24:45

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

    private void addGlobalClickListener() {
        weakAwtEventListener = new WeakAWTEventListener(this);

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

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

        if(weakAwtEventListener != null) {
            Toolkit.getDefaultToolkit().removeAWTEventListener(weakAwtEventListener);
        }
    }
 2
Author: Alexander Malfait,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-07 08:11:40

Tenga cuidado con lo que hace en un finalize(). Especialmente si lo está usando para cosas como llamar a close() para asegurarse de que los recursos se limpien. Nos encontramos con varias situaciones en las que teníamos bibliotecas JNI enlazadas al código java en ejecución, y en cualquier circunstancia en la que usáramos finalize() para invocar métodos JNI, obtendríamos una corrupción de montón de java muy mala. 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. Fue solo el hecho de que estábamos llamando a los métodos JNI desde finalize () en absoluto.

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

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

 1
Author: Steven M. Cherry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-01 16:00:22

Iirc - puede usar el método finalize como un medio para implementar un mecanismo de agrupación de recursos costosos, para que no obtengan también GC.

 0
Author: JGFMK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-19 17:01:58

Las listas de respuestas aceptadas que el cierre de un recurso durante la finalización se puede hacer.

Sin embargo esta respuesta muestra que al menos en java8 con el compilador JIT, te encuentras con problemas inesperados donde a veces se llama al finalizador incluso antes de que termines de leer un flujo mantenido por tu objeto.

Así que incluso en esa situación no se recomendaría llamar a finalize.

 0
Author: Joeblade,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:08

Personalmente, casi nunca uso finalize() excepto en una rara circunstancia: 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 te permite especificar que has implementado métodos Object raramente implementados como#finalize(), #hashCode(), y #clone())

Entonces, usando un método sister #setDestructivelyFinalizes(boolean), el programa que usa mi colección puede (ayudar) garantizar que destruir una referencia a esta colección también destruye las referencias a su contenido y elimina cualquier ventana que pueda mantener viva la JVM sin querer. Consideré también detener cualquier hilo, pero eso abrió una nueva lata de gusanos.

 0
Author: Supuhstar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-16 16:36:52

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

En Java 7 tenemos la instrucción 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. IMO parece más limpio y sencillo de entender.

 0
Author: i_am_zero,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-24 03:54:26

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 después de que el objeto ya no está en el alcance. Sin embargo, los objetos finalizables se mueven a una cola, donde los subprocesos de finalización separados drenarán la cola y ejecutarán el método finalize() en cada objeto. Una vez que el método finalize() termine, el objeto estará listo para la recolección de basura en el próximo ciclo.

Finalizar obsoleto en java-9

 0
Author: Sudip Bhandari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-04 07:20:37