¿Hay un destructor para Java?

¿Hay un destructor para Java? Parece que no puedo encontrar ninguna documentación sobre esto. Si no hay, ¿cómo puedo lograr el mismo efecto?

Para hacer que mi pregunta sea más específica, estoy escribiendo una aplicación que trata con datos y las especificaciones dicen que debe haber un botón ‘reiniciar’ que devuelva a la aplicación a su estado original recién lanzado. Sin embargo, todos los datos tienen que estar ‘en vivo’ a menos que la aplicación esté cerrada o se presione el botón de reinicio.

Siendo usualmente un progtwigdor de C / C ++, pensé que sería trivial implementarlo. (Y, por tanto, planeé implementarlo al final.) Estructuraba mi progtwig de modo que todos los objetos “reiniciables” estuvieran en la misma clase, de manera que solo puedo destruir todos los objetos “activos” cuando se presiona un botón de reinicio.

Estaba pensando que si todo lo que hacía era simplemente eliminar los datos y esperar a que el recolector de basura los recogiera, ¿no habría una pérdida de memoria si mi usuario ingresara datos repetidamente y presionara el botón de reinicio? También pensaba que, dado que Java es bastante maduro como lenguaje, debería haber una manera de evitar que esto ocurra o abordarlo con gracia.

Dado que Java es un lenguaje recogido de basura, no puede predecir cuándo (o incluso si) se destruirá un objeto. Por lo tanto, no existe un equivalente directo de un destructor.

Hay un método heredado llamado finalize , pero esto se llama completamente a discreción del recolector de basura. Por lo tanto, para las clases que necesitan organizarse explícitamente, la convención es definir un método de cierre y usar finalizar solo para la comprobación de cordura (es decir, si no se ha llamado cerrar, hágalo ahora y registre un error).

Hubo una pregunta que engendró un debate en profundidad sobre la finalización recientemente, por lo que debería proporcionar más profundidad si fuera necesario …

No, no hay destructores aquí. La razón es que todos los objetos de Java se asignan en el montón y se recolectan los desechos. Sin una desasignación explícita (es decir, el operador de eliminación de C ++) no existe una forma sensata de implementar destructores reales.

Java admite finalizadores, pero están destinados a ser utilizados solo como salvaguarda de objetos que manejan recursos nativos como sockets, manejadores de archivos, manejadores de ventanas, etc. Cuando el recolector de basura recoge un objeto sin un finalizador simplemente marca la memoria región como libre y eso es todo. Cuando el objeto tiene un finalizador, primero se copia en una ubicación temporal (recuerde, estamos recolectando basura aquí), luego se pone en una cola en espera de ser finalizada y luego un hilo de Finalizer sondea la cola con muy baja prioridad y ejecuta el finalizador.

Cuando la aplicación finaliza, la JVM se detiene sin esperar a que finalicen los objetos pendientes, por lo que prácticamente no hay garantías de que se ejecuten los finalizadores.

Si usa Java 7, eche un vistazo a la statement try-with-resources . Por ejemplo:

 try (BufferedReader br = new BufferedReader(new FileReader(path))) { System.out.println(br.readLine()); } catch (Exception e) { ... } finally { ... } 

Aquí el recurso que ya no se necesita se libera en el método BufferedReader.close() . Puede crear su propia clase que implemente AutoCloseable y usarla de manera similar.

Esta statement es más limitada que la finalize en términos de estructuración del código, pero al mismo tiempo hace que el código sea más simple de comprender y mantener. Además, no hay garantía de que se invoque un método de finalize durante el tiempo de actividad de la aplicación.

El uso de los métodos de finalize () debe ser evitado. No son un mecanismo confiable para la limpieza de recursos y es posible causar problemas en el recolector de basura al abusar de ellos.

Si necesita una llamada de desasignación en su objeto, por ejemplo para liberar recursos, use una llamada a un método explícito. Esta convención se puede ver en API existentes (por ejemplo, Cerrar , Graphics.dispose () , Widget.dispose () ) y generalmente se llama a través de try / finally.

 Resource r = new Resource(); try { //work } finally { r.dispose(); } 

Los bashs de utilizar un objeto eliminado deben arrojar una excepción de tiempo de ejecución (vea IllegalStateException ).


EDITAR:

Estaba pensando, si todo lo que hacía era simplemente quitar los datos y esperar a que el recolector de basura los recogiera, ¿no habría una pérdida de memoria si mi usuario ingresara datos repetidamente y presionara el botón de reinicio?

Generalmente, todo lo que necesita hacer es eliminar los objetos, al menos, así se supone que debe funcionar. Si está preocupado por la recolección de basura, consulte Java S6 HotSpot [tm] Virtual Machine Garbage Collection Tuning (o el documento equivalente para su versión de JVM).

Con Java 1.7 liberado, ahora tiene la opción adicional de usar el bloque try-with-resources . Por ejemplo,

 public class Closeable implements AutoCloseable { @Override public void close() { System.out.println("closing..."); } public static void main(String[] args) { try (Closeable c = new Closeable()) { System.out.println("trying..."); throw new Exception("throwing..."); } catch (Exception e) { System.out.println("catching..."); } finally { System.out.println("finalizing..."); } } } 

Si ejecuta esta clase, c.close() se ejecutará cuando se deje el bloque try , y antes de que se ejecuten los bloques catch y finally . A diferencia del caso del método finalize() , se garantiza que se ejecutará close() . Sin embargo, no hay necesidad de ejecutarlo explícitamente en la cláusula finally .

Estoy totalmente de acuerdo con otras respuestas, diciendo que no confíe en la ejecución de finalizar.

Además de los bloques try-catch-finally, puede usar Runtime # addShutdownHook (introducido en Java 1.6) para realizar limpiezas finales en su progtwig.

Eso no es lo mismo que los destructores , pero uno puede implementar un enganche de cierre que tenga objetos de escucha registrados en los que se puedan invocar los métodos de limpieza (cerrar conexiones de bases de datos persistentes, eliminar lockings de archivos, etc.), cosas que normalmente se realizarían en Destructores . Nuevamente, esto no reemplaza a los constructores, pero en algunos casos, puede acercarse a la funcionalidad deseada con esto.

La ventaja de esto es tener un comportamiento de desconstrucción estrechamente relacionado con el rest de su progtwig.

No, java.lang.Object#finalize es lo más cercano que puede obtener.

Sin embargo, cuando (y si) se llama, no está garantizado.
Ver: java.lang.Runtime#runFinalizersOnExit(boolean)

En primer lugar, tenga en cuenta que, dado que Java se recolecta basura, es raro que necesite hacer algo sobre la destrucción de objetos. En primer lugar, porque normalmente no tiene ningún recurso administrado para liberar, y en segundo lugar porque no puede predecir cuándo o si sucederá, por lo que no es apropiado para las cosas que debe ocurrir “tan pronto como nadie esté utilizando mi objeto”. “.

Puede recibir una notificación después de destruir un objeto utilizando java.lang.ref.PhantomReference (en realidad, decir que se ha destruido puede ser un poco impreciso, pero si se pone en cola una referencia fantasma, ya no es recuperable, lo que generalmente equivale a la misma cosa). Un uso común es:

  • Separe los recursos de su clase que necesitan ser destruidos en otro objeto auxiliar (tenga en cuenta que si todo lo que hace es cerrar una conexión, que es un caso común, no necesita escribir una nueva clase: la conexión que se cerrará sería el “objeto auxiliar” en ese caso).
  • Cuando creas tu objeto principal, crea también una Referencia Phantom a él. O haga que esto se refiera al nuevo objeto auxiliar, o configure un mapa desde los objetos PhantomReference a sus objetos auxiliares correspondientes.
  • Después de recostackr el objeto principal, PhantomReference se pone en cola (o más bien se puede poner en la cola, como los finalizadores no hay garantía de que alguna vez lo será, por ejemplo, si la máquina virtual sale entonces no esperará). Asegúrate de que estás procesando su cola (ya sea en un hilo especial o de vez en cuando). Debido a la referencia difícil al objeto auxiliar, el objeto auxiliar aún no se ha recostackdo. Así que haz la limpieza que quieras en el objeto auxiliar, luego descarta la referencia fantasma y el ayudante eventualmente será recogido también.

También hay finalize (), que se ve como un destructor pero no se comporta como uno. Por lo general, no es una buena opción.

Lo siento si esto se desvía del tema principal, pero la documentación de java.util.Timer (SE6) dice:

“Después de que la última referencia en vivo a un objeto Timer desaparece y todas las tareas pendientes han completado la ejecución, el hilo de ejecución de tareas del temporizador finaliza con elegancia (y queda sujeto a la recolección de elementos no utilizados). Sin embargo, esto puede tomar mucho tiempo. El subproceso de ejecución de tareas no se ejecuta como un subproceso de daemon, por lo que es capaz de evitar que finalice una aplicación. Si un llamante desea finalizar rápidamente el subproceso de ejecución de tareas de un temporizador, el llamador debe invocar el método de cancelación del temporizador … ”

Me gustaría llamar cancelar sobre la clase que posee el temporizador perdiendo su última referencia (o immeditalesky antes). Aquí un destructor confiable podría hacer eso por mí. Los comentarios anteriores indican que finalmente es una mala elección, pero ¿existe una solución elegante? Ese asunto de “… capaz de evitar que una aplicación termine …” no es atractivo.

Si solo te preocupa la memoria, no lo hagas. Solo confíe en que el GC hace un trabajo decente. De hecho, vi algo sobre el hecho de que es tan eficiente que podría ser mejor para el rendimiento crear montones de objetos pequeños que utilizar arreglos grandes en algunos casos.

La función finalize() es el destructor.

Sin embargo, no debería utilizarse normalmente porque se invoca después del GC y no se puede saber cuándo sucederá (si es que lo hace).

Además, se necesita más de un GC para desasignar objetos que tienen finalize() .

Debería intentar limpiar en los lugares lógicos de su código usando las declaraciones try{...} finally{...} .

Estoy de acuerdo con la mayoría de las respuestas.

No debe depender completamente de finalize o ShutdownHook

finalizar

  1. JVM no garantiza cuándo se invocará este método de finalize() .

  2. finalize() se llama una sola vez por el hilo del GC si el objeto se restablece a sí mismo desde el método de finalización que no se volverá a llamar.

  3. En su aplicación, puede tener algunos objetos en vivo, en los que nunca se invoca recolección de basura.

  4. Cualquier excepción se produce al finalizar el método es ignorado por el hilo GC

  5. System.runFinalization(true) y Runtime.getRuntime().runFinalization(true) aumentan la probabilidad de invocar el método finalize() pero ahora estos dos métodos han quedado en desuso. Estos métodos son muy peligrosos debido a la falta de seguridad del hilo y la posible creación de interlockings.

shutdownHooks

 public void addShutdownHook(Thread hook) 

Registra un nuevo gancho de cierre de máquina virtual.

La máquina virtual Java se apaga en respuesta a dos tipos de eventos:

  1. El progtwig sale normalmente, cuando sale el último hilo no daemon o cuando se invoca el método exit (equivalentemente, System.exit), o
  2. La máquina virtual finaliza en respuesta a una interrupción del usuario, como escribir ^ C, o un evento en todo el sistema, como el cierre de sesión del usuario o el cierre del sistema.
  3. Un gancho de cierre es simplemente un hilo inicializado pero no iniciado. Cuando la máquina virtual comienza su secuencia de apagado, iniciará todos los enganches de apagado registrados en algún orden no especificado y los dejará funcionar al mismo tiempo. Cuando todos los enganches hayan finalizado, se ejecutarán todos los finalizadores no invocados si se ha habilitado la finalización a la salida.
  4. Finalmente, la máquina virtual se detendrá. Tenga en cuenta que los hilos daemon continuarán ejecutándose durante la secuencia de apagado, al igual que los hilos no daemon si el cierre se inició invocando el método de salida.
  5. Los ganchos de cierre también deberían terminar su trabajo rápidamente. Cuando un progtwig invoca la salida, la expectativa es que la máquina virtual se apague y salga de inmediato.

    Pero incluso la documentación de Oracle citó eso

En circunstancias excepcionales, la máquina virtual puede abortar, es decir, dejar de funcionar sin cerrarla limpiamente.

Esto ocurre cuando la máquina virtual finaliza externamente, por ejemplo con la señal SIGKILL en Unix o la llamada TerminateProcess en Microsoft Windows. La máquina virtual también puede abortar si un método nativo falla al, por ejemplo, corromper las estructuras internas de datos o intentar acceder a una memoria inexistente. Si la máquina virtual aborta, no se puede garantizar si se ejecutarán o no los ganchos de cierre.

Conclusión : utilice los bloques try{} catch{} finally{} adecuada y libere recursos críticos en el bloque finally(} . Durante el lanzamiento de los recursos en el bloque finally{} , capture Exception y Throwable .

Quizás puedas usar un bloque try … finally para finalizar el objeto en el flujo de control en el que estás usando el objeto. Por supuesto, no sucede automáticamente, pero tampoco la destrucción en C ++. A menudo ve el cierre de recursos en el bloque finally.

El equivalente más cercano a un destructor en Java es el método finalize () . La gran diferencia para un destructor tradicional es que no puede estar seguro de cuándo será llamado, ya que esa es la responsabilidad del recolector de basura. Recomiendo leer detenidamente esto antes de usarlo, ya que sus patrones RAIA típicos para manejadores de archivos y demás no funcionarán de manera confiable con finalize ().

Hay una anotación @Cleanup en Lombok que en su mayoría se parece a los destructores de C ++:

 @Cleanup ResourceClass resource = new ResourceClass(); 

Al procesarlo (en tiempo de comstackción), Lombok inserta el bloque try-finally apropiado para que invoque a resource.close() , cuando la ejecución abandona el scope de la variable. También puede especificar explícitamente otro método para liberar el recurso, por ejemplo, resource.dispose() :

 @Cleanup("dispose") ResourceClass resource = new ResourceClass(); 

Aunque ha habido avances considerables en la tecnología GC de Java, aún debe tener en cuenta sus referencias. Numerosos casos de patrones de referencia aparentemente triviales que en realidad son nidos de ratas bajo el capó vienen a la mente.

Desde su publicación, no parece que intente implementar un método de reinicio para fines de reutilización de objetos (¿cierto?). ¿Sus objetos contienen algún otro tipo de recursos que necesitan ser limpiados (es decir, flujos que deben cerrarse, cualquier objeto mancomunado o prestado que debe devolverse)? Si lo único que te preocupa es que la memoria funcione, entonces reconsideraría mi estructura de objetos e intentaría verificar que mis objetos sean estructuras independientes que se limpiarán en el momento del GC.

Si está escribiendo un Applet de Java, puede anular el método “destroy ()” de Applet. Es…

  * Called by the browser or applet viewer to inform * this applet that it is being reclaimed and that it should destroy * any resources that it has allocated. The stop() method * will always be called before destroy(). 

Obviamente no es lo que quieres, pero podría ser lo que otras personas están buscando.

Solo pensando en la pregunta original … que, creo que podemos concluir a partir de todas las otras respuestas aprendidas, y también del Essential Effective Java de Bloch, el ítem 7, “Avoid finalizers”, busca la solución a una pregunta legítima de una manera que es inapropiado para el lenguaje Java …

… no sería una solución bastante obvia para hacer lo que el OP realmente desea mantener todos sus objetos que deben ser reiniciados en una especie de “corralito”, a la que todos los demás objetos no reseteables solo tienen referencias a través de algún tipo de objeto de acceso …

Y luego, cuando necesites “reiniciar”, desconectas el corralito existente y creas uno nuevo: toda la red de objetos en el parque se lanza a la deriva, nunca regresa y un día lo recoge el GC.

Si alguno de estos objetos es Closeable (o no, pero tiene un método de close ), puede ponerlos en una Bag en el corralito a medida que se crean (y posiblemente se abren), y el último acto del accesorio antes de cortar el corral sería pasar por todos los Closeables cerrándolos …?

El código probablemente se vería así:

 accessor.getPlaypen().closeCloseables(); accessor.setPlaypen( new Playpen() ); 

closeCloseables probablemente sea un método de locking, que probablemente involucre un pestillo (por ejemplo, CountdownLatch ), para tratar (y esperar según corresponda) cualquier Runnables / Callables en cualquier subproceso específico del Playpen para finalizar según corresponda, en particular en el hilo JavaFX .

No existe una clase de destructor exactamente en Java, clase destruida en java automáticamente por el recolector de basura. pero podrías hacer eso usando debajo de uno, pero no es exactamente lo mismo:

finalizar()

Hubo una pregunta que generó un debate en profundidad sobre la finalización , por lo que debería obtener más profundidad si fuera necesario …

Solía ​​tratar principalmente con C ++ y eso es lo que me llevó a la búsqueda de un destructor también. Estoy usando mucho JAVA ahora. Lo que hice, y puede que no sea el mejor caso para todos, pero implementé mi propio destructor restableciendo todos los valores a 0 o de forma predeterminada a través de una función.

Ejemplo:

 public myDestructor() { variableA = 0; //INT variableB = 0.0; //DOUBLE & FLOAT variableC = "NO NAME ENTERED"; //TEXT & STRING variableD = false; //BOOL } 

Idealmente, esto no funcionará para todas las situaciones, pero donde haya variables globales funcionará siempre y cuando no tenga una gran cantidad de ellas.

Sé que no soy el mejor progtwigdor de Java, pero parece estar funcionando para mí.

    Intereting Posts