Finalisierung wird verwendet, um Ressourcen zu bereinigen, die vom Garbage Collector nicht freigegeben werden können. Betrachten Sie zum Beispiel ein Programm, das Ressourcen (über einige native
API) direkt aus dem Betriebssystem zuweist. Daraus ergibt sich in der Regel eine Art von „Handle“ (ein UNIX-Dateideskriptor oder Windows-Handle, oder so ähnlich):
class Wrapper {
private long handle;
private Handle(long h) {
handle = h;
}
private static native long getHandleFromOS();
static Wrapper allocate() {
return new Handle(getHandleFromOS());
}
}
Also, was passiert, wenn Ihr Code eine Instanz der Klasse ordnet Wrapper
? Nun, die Klasse weist irgendeine Art von OS-spezifischer Ressource zu und behält einen Verweis auf sie (das Handle) in einer Elementvariablen bei. Was aber passiert, wenn der letzte Java-Verweis auf eine Wrapper-Instanz verloren geht? Jetzt wird der Garbage Collector (irgendwann) den Speicherplatz der nun nicht mehr vorhandenen Wrapper-Instanz zurückfordern. Was aber passiert mit der vom Wrapper zugewiesenen OS-Ressource? Es wird im obigen Szenario geleakt, was eine schlechte Sache ist, wenn es eine kostspielige Ressource ist, wie zum Beispiel ein Dateideskriptor.
Damit Ihr Code in einem solchen Szenario aufgeräumt werden kann, gibt es die Methode 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());
}
}
Wenn nun der GC den Raum eines Wrapper-Instanz reklamiert, macht die Finalizerthread sicher, dass die Ressource ordnungsgemäß an das OS zurückgegeben wird.
Das hört sich alles gut an, aber wie andere bereits angemerkt haben, ist der Nachteil, dass die Finalisierung inhärent unzuverlässig ist: Sie wissen nicht, wann der Finalizer ausgeführt wird. Schlimmer noch: Es gibt keine Garantie, dass es überhaupt ausgeführt wird. So ist am besten, einen dispose
Mechanismus zu schaffen, und verwenden Finalisierung nur als Sicherheitsnetz für den Fall, vergessen die Kunden Ihrer Klasse richtig ihre Referenzen verfügen:
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());
}
}
Sie nicht „wenn und nur wenn“ sagen kann; Sie können sagen "nur wenn", aber es gibt keine Garantie, dass es überhaupt aufgerufen wird. –
@ mmyers: +1. zurückgefordert -> finalisieren wurde ausgeführt, aber nicht umgekehrt. – danben
Dort gibt es kein "aber": Wenn der GC versucht, den Speicher zurückzufordern, wird finalize() aufgerufen. –