He estado leyendo muchas de las preguntas de Java para novatos finalize()
y me parece desconcertante que nadie haya dejado en claro que finalize () es una forma poco confiable de limpiar recursos. Vi a alguien comentar que lo usan para limpiar Connections, lo cual es realmente aterrador ya que la única forma 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 durante casi una década y nunca he visto a nadie implementarlo 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 hay para implementar finalize()
que no puedan manejarse de manera más confiable a través de otro proceso o sintaxis dentro del lenguaje?
Proporcione escenarios específicos o su experiencia, simplemente repitiendo un libro de texto Java o finalizando el uso previsto no es suficiente, ya que no es la intención de esta pregunta.
finalize()
. Sin embargo, el código de la biblioteca de la plataforma , comoSocketInputStream
, que gestiona los recursos nativos en nombre de la persona que llama, lo hace para tratar de minimizar el riesgo de pérdidas de recursos (o utiliza mecanismos equivalentes, como losPhantomReference
que se agregaron más adelante). Por lo tanto, el ecosistema los necesita , aunque el 99.9999% de los desarrolladores nunca escribirán uno.Respuestas:
Puede usarlo como respaldo para un objeto que contiene un recurso externo (socket, archivo, etc.). Implemente un
close()
método y documento al que deba llamarse.Implemente
finalize()
para realizar elclose()
procesamiento si detecta que no se ha realizado. Tal vez con algo tirado parastderr
señalar que estás limpiando después de una llamada con errores.Proporciona seguridad adicional en una situación excepcional / con errores. No todas las personas que llaman van a hacer lo correcto
try {} finally {}
cada vez. Desafortunado, pero cierto en la mayoría de los entornos.Estoy de acuerdo en que rara vez se necesita. Y como señalan los comentaristas, viene con una sobrecarga de GC. Ú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ñalanjava.lang.ref.Cleaner
yjava.lang.ref.PhantomReference
como alternativas.fuente
finalize()
es una pista 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 no se ejecute misteriosamente.Hacer algo significativo en los finalizadores (básicamente cualquier cosa excepto el registro) también es bueno en tres situaciones:
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 sólida a una conexión utilizada 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 ejecutará, pero al menos no puede invocar métodos o resucitar objetos finalizados. Por lo tanto, es adecuado para situaciones en las que no es absolutamente necesario cerrar esa conexión limpiamente, pero le gustaría hacerlo, y los clientes de su clase no pueden o no llamarán a sí mismos (lo que en realidad es bastante justo: qué' ¿Es el punto de tener un recolector de basura si diseña interfaces que requieren una acción específica antes de la recolección? Eso solo nos devuelve a los días de malloc / free.)
Otras veces necesita el recurso que cree que está logrando para ser más robusto. Por ejemplo, ¿por qué necesita cerrar esa conexión? En última instancia, debe basarse en algún tipo de E / S proporcionadas por el sistema (socket, archivo, lo que sea), entonces, ¿por qué no puede confiar en que el sistema lo cierre por usted cuando el nivel más bajo de recursos está ajustado? Si el servidor en el otro extremo requiere absolutamente que cierre la conexión limpiamente en lugar de simplemente dejar caer el zócalo, entonces ¿qué sucederá cuando alguien tropiece con el cable de alimentación de la máquina en la que se está ejecutando su código o se interrumpe la red?
Descargo de responsabilidad: he trabajado en una implementación de JVM en el pasado. Odio los finalizadores.
fuente
finalize()
sin dar ninguna razón, alguien realmente querría usarlo.java.lang.ref.Reference
y sus subclases. Java 1 tenía variables :-) Y sí, también tenía finalizadores.Una regla simple: nunca use finalizadores.
Solo el hecho de que un objeto tiene 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:
fuente
La única vez que utilicé finalizar en el código de producción fue implementar una verificación de que los recursos de un objeto dado se habían limpiado, y si no, registrar un mensaje muy vocal. En realidad no lo intentó y lo hizo por sí mismo, solo gritaba mucho si no se hacía correctamente. Resultó ser bastante útil.
fuente
He estado haciendo Java profesionalmente desde 1998, y nunca lo he implementado
finalize()
. Ni una sola vez.fuente
La respuesta aceptada es buena, solo quería agregar que ahora hay una manera de tener la funcionalidad de finalizar sin usarla en absoluto.
Mire las clases de "Referencia". Referencia débil, Phantom Reference y Soft Reference.
Puede usarlos para mantener una referencia a todos sus objetos, pero esta referencia SOLO no detendrá el 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 .
En cuanto a finalizar: utilicé finalizar una vez para comprender qué objetos se estaban liberando. Puedes jugar algunos juegos divertidos con estadísticas, conteo de referencias y demás, pero fue solo para análisis, pero ten cuidado con un código como este (no solo en finalización, sino que es donde es más probable que lo veas):
Es una señal de que alguien no sabía lo que estaban haciendo. "Limpiar" de esta manera prácticamente nunca es necesario. Cuando la clase es GC'd, esto se hace automáticamente.
Si encuentra un código como ese en una finalización, se garantiza que la persona que lo escribió estaba confundida.
Si está en otra parte, podría ser que el código es un parche válido para un modelo incorrecto (una clase permanece durante mucho tiempo y, por alguna razón, las cosas a las que hace referencia tuvieron que liberarse manualmente antes de que el objeto sea GC'd). En general, es porque alguien olvidó eliminar a un oyente o algo y no puede entender por qué su objeto no está siendo GC, por lo que simplemente borran las cosas a las que se refiere y se encogen de hombros y se van.
Nunca debe usarse para limpiar cosas "más rápido".
fuente
No estoy seguro de qué puedes hacer con esto, pero ...
Así que supongo que el Sol encontró algunos casos en los que (piensan) debería usarse.
fuente
finalize
lugar de usarlo realmente?$FAVORITE_IDE
.====================================
resultado:
=====================================
Por lo tanto, puede hacer que una instancia inalcanzable sea accesible en el método de finalización.
fuente
System.gc();
no es una garantía de que el recolector de basura realmente se ejecutará. Es la total discreción del GC limpiar el montón (quizás todavía haya suficiente montón libre, tal vez no esté demasiado fragmentado, ...). Entonces, es solo una humilde petición del programador "por favor, ¿podrían escucharme y salir corriendo?" y no un comando "¡corre ahora como digo!". Perdón por usar palabras linguales, pero dice exactamente cómo funciona el GC de JVM.finalize()
Puede ser útil para detectar fugas de recursos. Si el recurso debe cerrarse pero no es así, escriba el hecho de que no se cerró en un archivo de registro y ciérrelo. De esa manera, 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 finalizar para cualquier cosa ...
fuente
No debe depender de finalize () para limpiar sus recursos por usted. finalize () no se ejecutará hasta que la clase se recolecte basura, si es así. Es mucho mejor liberar recursos explícitamente cuando haya terminado de usarlos.
fuente
Para resaltar un punto en las respuestas anteriores: los finalizadores se ejecutarán en el único hilo GC. He oído hablar de una importante demostración de Sun en la que los desarrolladores agregaron una pequeña suspensión a algunos finalizadores e intencionalmente pusieron de rodillas una demo 3D de otro modo elegante.
Es mejor evitarlo, con la posible excepción de los diagnósticos test-env.
Eckel's Thinking in Java tiene una buena sección sobre esto.
fuente
Hmmm, una vez lo usé para limpiar objetos que no estaban siendo devueltos a un grupo existente.
Se les pasaba mucho, por lo que era imposible saber cuándo podrían regresar con seguridad a la piscina. 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 grupo, hiciera todo dinámico y terminé.
fuente
Ten cuidado con lo que haces en a
finalize()
. Especialmente si lo está utilizando para cosas como llamar a close () para asegurarse de que se limpien los recursos. 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 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. Era solo el hecho de que estábamos llamando a los métodos JNI desde finalize ().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 finalize () haciendo uso de llamadas JNI.
fuente
Al escribir código que será utilizado por otros desarrolladores que requiera algún tipo de método de "limpieza" para liberar recursos. A veces, esos otros desarrolladores se olvidan de llamar a su método de limpieza (o cierre, o destrucción, o lo que sea). Para evitar posibles fugas de recursos, puede verificar el método de finalización para asegurarse de que se llamó al método y, si no fue así, puede llamarlo usted mismo.
Muchos controladores de bases de datos hacen esto en sus implementaciones de Declaración y Conexión para proporcionar un poco de seguridad contra los desarrolladores que olvidan llamarlos de cerca.
fuente
Editar: Ok, realmente no funciona. Lo implementé y pensé que si falla a veces, eso 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 buen ejemplo de uso de finalize (), que es un caché que escribe su contenido en el disco antes de que se destruya. Como no es necesario que se ejecute cada vez que se destruye, solo acelera mi programa, espero que no lo haya hecho mal.
fuente
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:
fuente
this
instancia que se le pasó en el constructor, esa instancia nunca será inalcanzable, por lo tantofinalize()
, nunca se limpiará de todos modos. Si, por otro lado, el oyente tiene una referencia débil a ese objeto, como su nombre indica, esta construcción corre el riesgo de limpiarse en momentos en que no debería. Los ciclos de vida de los objetos no son la herramienta adecuada para implementar la semántica de la aplicación.iirc: puede usar el método de finalización como un medio para implementar un mecanismo de agrupación de recursos caros, para que no obtengan GC también.
fuente
Como nota al margen:
Fuente: finalize () en desuso en java-9
fuente
Los recursos (Archivo, Socket, Stream, etc.) deben cerrarse una vez que hayamos terminado con ellos. Generalmente tienen un
close()
método que generalmente llamamos en lafinally
sección detry-catch
declaraciones. Algunas vecesfinalize()
también pueden ser utilizados por pocos desarrolladores, pero la OMI no es una forma adecuada, ya que no hay garantía de que finalice siempre.En Java 7 tenemos una declaración de prueba con recursos que se puede usar como:
En el ejemplo anterior, try-with-resource cerrará automáticamente el recurso
BufferedReader
invocando elclose()
método. Si queremos, también podemos implementar Closeable en nuestras propias clases y usarlo de manera similar. En mi opinión, parece más ordenado y simple de entender.fuente
Personalmente, casi nunca lo uso,
finalize()
excepto en una circunstancia rara: hice una colección personalizada de tipo genérico y escribí unfinalize()
método personalizado que hace lo siguiente:(
CompleteObject
Es una interfaz que hice que le permite especificar que ha implementado implementadas raramenteObject
métodos como#finalize()
,#hashCode()
y#clone()
)Entonces, usando un
#setDestructivelyFinalizes(boolean)
método hermano , el programa que usa mi colección puede (ayudar) a garantizar que la destrucción de una referencia a esta colección también destruya las referencias a su contenido y elimine cualquier ventana que pueda mantener viva la JVM sin querer. También consideré detener cualquier subproceso, pero eso abrió una nueva lata de gusanos.fuente
finalize()
a susCompleteObject
instancias como lo hace en el código anterior implica que podría llamarse dos veces, ya que la JVM aún podría invocarfinalize()
una vez que estos objetos se vuelvan inalcanzables, lo que, debido a la lógica de finalización, podría estar antes delfinalize()
método de su colección especial se llama o incluso simultáneamente al mismo tiempo. Como no veo ninguna medida para garantizar la seguridad de los hilos en su método, parece que no se da cuenta de esto ...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 compilador JIT, se encuentra con problemas inesperados en los que a veces se llama al finalizador incluso antes de terminar de leer una secuencia mantenida por su objeto.
Por lo tanto, incluso en esa situación, no se recomendaría finalizar .
fuente