2010-03-15 11 views
9

Mi comprensión de la finalización es la siguiente:¿Cuál es el propósito de la finalización en Java?

Para limpiar o recuperar la memoria que ocupa un objeto, el recolector de basura entra en acción. (¿se invoca automáticamente?)

El recolector de elementos no utilizados desentierra el objeto. A veces, no hay forma de que el recolector de basura acceda al objeto. A continuación, se invoca finalize para realizar un proceso de limpieza final después del cual se puede invocar el recolector de elementos no utilizados.

¿Es esta una descripción exacta de la finalización?

Respuesta

15

El recolector de basura funciona automáticamente en segundo plano (aunque se puede invocar explícitamente, pero la necesidad de esto debe ser rara). Básicamente limpia solo los objetos que no son referenciados por otros objetos (se concede, la imagen completa es más complicada, pero esta es la idea básica). Por lo tanto, no cambia ninguna referencia en ningún objeto en vivo. Si no se puede acceder a un objeto desde ningún objeto vivo, esto significa que se puede recolectar basura de forma segura.

Finalización fue destinado a limpiar los recursos adquiridos por el objeto (no memoria, pero otros recursos, por ejemplo, identificadores de archivos, puertos, conexiones de bases de datos, etc.). Sin embargo, en realidad no funciona :-(

  • es impredecible cuando finalize() se llamará
  • , de hecho, no hay ninguna garantía de que finalize() se llamará nunca!

Así que incluso si se garantiza que se llamará, no sería un buen lugar para liberar recursos: cuando se llame para liberar todas las conexiones de base de datos que haya abierto, es posible que el sistema se haya quedado sin conexiones gratuitas por completo, y su aplicación ya no funciona.

6

No. El método finalize() se ejecuta solo si el recolector de basura intenta reclamar su objeto.

Cualquier memoria utilizada por su objeto (por lo general, no se me ocurre una excepción) se conectará automáticamente a su objeto y se limpiará junto con ella. La finalización, por lo tanto, no está pensada para liberar la memoria , sino más bien cualquier otro recurso al que pueda estar asociado su objeto. Por ejemplo, esto podría usarse para cerrar archivos abiertos o conexiones de bases de datos, o quizás ejecutar alguna interfaz de código de bajo nivel con el sistema operativo para liberar algunos recursos a nivel del sistema.

+1

No se puede decir "si y sólo si"; puede decir "solo si", pero no hay garantía de que se llame. –

+0

@mmyers: +1. reclaimed -> finalize ha sido ejecutado, pero no al revés. – danben

+0

No hay "sino" allí: si el GC intenta reclamar la memoria, entonces se llama a finalize(). –

7

De this article:

todas las instancias de clases que implementan la método finalize() son a menudo llamados objetos finalizables. Ellos no serán recuperados inmediatamente por el recolector de basura de Java cuando ya no se hace referencia. En su lugar, el recolector de elementos no utilizados de Java anexa los objetos a una cola especial para el proceso de finalización . Por lo general, es realizado por un hilo especial llamado "Controlador de referencia" en algunas máquinas virtuales Java . Durante este proceso de finalización , el subproceso "Finalizer" ejecutará cada método de finalización() de los objetos.Solo después de finalización exitosa del método finalize() un objeto será entregado para la recolección de basura de Java para obtener su espacio reclamado por recolección de basura "futura".

Usted es libre de hacer prácticamente cualquier cosa en el método finalize() de su clase . Cuando lo haga, no solicite el espacio de memoria ocupado por por cada objeto reclamado por el recolector de basura de Java cuando ya no se hace referencia al objeto o no hace falta . ¿Por qué? No es garantizado que el método finalize() completará la ejecución a tiempo manera. En el peor de los casos, puede que ni siquiera se invoque incluso cuando no haya más referencias al objeto . Eso significa no se garantiza que cualquier objeto que tenga un método de finalización() sea basura recolectada.

Además, this article de Sun tiene algunos diagramas que explican el proceso.

5

En realidad, éste es el comportamiento del método finalize():

Una vez que se ejecuta el recolector de basura (la máquina virtual decide que necesita para liberar memoria, no se puede obligar a que se ejecute) y decidió recoger la memoria de este objeto (lo que significa que ya no hay referencias que lo señalen, desde objetos alcanzables al menos), justo antes de que borre la memoria ocupada por él, ejecuta el método finalize() en el objeto. Puede estar seguro de que si se recolecta basura, el objeto se ejecutará finalize() justo antes de que desaparezca, pero no puede estar seguro de que recibirá GC así que no debe confiar en que el método lo desinfecte en absoluto. . Debería ejecutar declaraciones de desinfección dentro de {} bloques y no usar finalize() ya que no se garantiza su ejecución.

Además, algunas personas han realizado pruebas de rendimiento y han demostrado que el método de finalización ralentiza la creación/destrucción del objeto. No recuerdo la fuente, así que trate esta información como poco confiable. :)

+0

Los finalizadores causaron problemas de rendimiento en NIO. Se eliminaron en 1.4.1 (o posiblemente 1.4.2). Si está utilizando NIO, se espera que sepa cómo usar 'finally' correctamente. –

2

La finalización se utiliza para limpiar recursos, que no pueden ser liberados por el recolector de elementos no utilizados. Por ejemplo, considere un programa que asigna (a través de algunos recursos native API) directamente desde el sistema operativo. Esto suele proporcionar algún tipo de "mango" (un descriptor de archivo UNIX o el mango de Windows, o algo similar):

class Wrapper { 
    private long handle; 

    private Handle(long h) { 
     handle = h; 
    } 

    private static native long getHandleFromOS(); 

    static Wrapper allocate() { 
     return new Handle(getHandleFromOS()); 
    } 
} 

Por lo tanto, lo que sucede, si su código asigna una instancia de la clase Wrapper? Bueno, la clase asigna algún tipo de recurso específico del sistema operativo y mantiene una referencia a él (el identificador) en una variable miembro. Pero, ¿qué ocurre cuando se pierde la última referencia de Java a una instancia de contenedor? Ahora, el recolector de basura recuperará (en algún momento) el espacio de la instancia de envoltura ahora difunta. Pero, ¿qué sucede con el recurso del sistema operativo asignado por el contenedor? Se filtró en el escenario anterior, lo cual es algo malo, si es un recurso costoso, como un descriptor de archivo.

Para permitir que su código se limpie en tal escenario, existe el método finalize.

class Wrapper { 
    private long handle; 

    private Handle(long h) { 
     handle = h; 
    } 

    protected void finalize() { 
     returnHandleToOS(handle); 
    } 

    private static native long getHandleFromOS(); 
    private static native void returnHandleToOS(long handle); 

    static Wrapper allocate() { 
     return new Handle(getHandleFromOS()); 
    } 
} 

Ahora, cuando el GC recupera el espacio de una instancia de envoltura, el finalizador se asegura, que el recurso se devuelve correctamente al sistema operativo.

Esto suena muy bien, pero como otros ya han señalado, la desventaja es que la finalización es inherentemente poco confiable: no se sabe cuándo se ejecutará el finalizador. Peor aún: no hay garantías de que se ejecutará en absoluto. Así que no está en la mejor manera de proporcionar un mecanismo dispose y el uso de finalización sólo como red de seguridad en caso de que, los clientes de su clase se olvide de disponer adecuadamente sus referencias:

class Wrapper { 
    private long handle; 

    private Handle(long h) { 
     handle = h; 
    } 

    protected void finalize() { 
     if(handle != 0) returnHandleToOS(handle); 
    } 

    public void dispose() { 
     returnHandleToOS(handle); 
     handle = 0; 
    } 

    private static native long getHandleFromOS(); 
    private static native void returnHandleToOS(long handle); 

    static Wrapper allocate() { 
     return new Handle(getHandleFromOS()); 
    } 
} 
Cuestiones relacionadas