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());
}
}
No se puede decir "si y sólo si"; puede decir "solo si", pero no hay garantía de que se llame. –
@mmyers: +1. reclaimed -> finalize ha sido ejecutado, pero no al revés. – danben
No hay "sino" allí: si el GC intenta reclamar la memoria, entonces se llama a finalize(). –