2010-03-15 12 views
9

Mein Verständnis der Finalisierung ist dies:Was ist der Zweck der Finalisierung in Java?

Um den Speicher, den ein Objekt belegt, aufzuräumen oder zurückzufordern, kommt der Garbage Collector zum Einsatz. (wird automatisch aufgerufen?)

Der Garbage Collector führt dann eine Dereferenzierung des Objekts durch. Manchmal hat der Garbage Collector keinen Zugriff auf das Objekt. Dann wird finalize aufgerufen, um eine abschließende Bereinigungsverarbeitung durchzuführen, nach der der Garbage Collector aufgerufen werden kann.

Ist dies eine genaue Beschreibung der Finalisierung?

Antwort

15

Der Garbage Collector arbeitet automatisch im Hintergrund (obwohl es explizit aufgerufen werden kann, aber die Notwendigkeit dafür sollte selten sein). Es räumt im Grunde nur Objekte auf, die nicht von anderen Objekten referenziert werden (das Gesamtbild ist komplizierter, aber das ist die Grundidee). Es ändert also keine Referenzen in Live-Objekten. Wenn auf ein Objekt von keinem Live-Objekt aus zugegriffen werden kann, bedeutet dies, dass es sicher mit Müll gesammelt werden kann.

Abschluss war gemeint, um Ressourcen zu bereinigen, die vom Objekt erfasst wurden (nicht Speicher, sondern andere Ressourcen, z. B. Dateihandles, Ports, DB-Verbindungen usw.). Allerdings konnte er sich nicht wirklich :-(trainieren

  • es nicht vorhersehbar ist, wenn finalize() wird
  • in der Tat genannt werden, gibt es keine Garantie dafür, dass finalize() jemals aufgerufen wird!

Also selbst Wenn es garantiert wäre, dass es aufgerufen wird, wäre es kein guter Ort, um Ressourcen freizugeben: bis es aufgerufen wird, um alle DB-Verbindungen freizugeben, die Sie geöffnet haben, hat das System möglicherweise keine freien Verbindungen mehr und Ihre App funktioniert nicht mehr

6

Nein. Die finalize()-Methode wird nur ausgeführt, wenn der Garbage Collector versucht, Ihr Objekt zurückzufordern.

Jeder von Ihrem Objekt verwendete Speicher wird (normalerweise kann ich nicht an eine Ausnahme denken) automatisch mit Ihrem Objekt verbunden und zusammen mit ihm gelöscht. Finalisierung ist daher nicht zum Freigeben von Speicher gedacht, sondern eher zu anderen Ressourcen, mit denen Ihr Objekt möglicherweise verbunden ist. Dies könnte beispielsweise verwendet werden, um offene Dateien oder Datenbankverbindungen zu schließen, oder um Code auf niedriger Ebene mit dem Betriebssystem zu verbinden, um einige Ressourcen auf Systemebene freizugeben.

+1

Sie nicht „wenn und nur wenn“ sagen kann; Sie können sagen "nur wenn", aber es gibt keine Garantie, dass es überhaupt aufgerufen wird. –

+0

@ mmyers: +1. zurückgefordert -> finalisieren wurde ausgeführt, aber nicht umgekehrt. – danben

+0

Dort gibt es kein "aber": Wenn der GC versucht, den Speicher zurückzufordern, wird finalize() aufgerufen. –

7

Von this article:

Alle Instanzen von Klassen, die die Finalisierung() -Methode sind oft genannt finalizable Objekte implementieren. Sie wird nicht sofort von der Java-Garbage Collector zurückgefordert, wenn sie nicht mehr verwiesen werden. Stattdessen fügt der Java-Garbage Collector die Objekte an eine spezielle Warteschlange für den Abschlussvorgang an. Normalerweise ist es durchgeführt von einem speziellen Thread namens "Reference Handler" auf einigen Java Virtual Machines. Während dieses finalen Prozesses wird der Thread "Finalizer" jede finalize() Methode der Objekte ausführen.Nur nach erfolgreicher Abschluss der finalize() -Methode wird ein Objekt übergeben für Java-Müll Sammlung, um seine Speicherplatz zurückgefordert von "Zukunft" Garbage Collection.

Sie können praktisch alles tun in der Finalize() - Methode Ihrer Klasse. Wenn Sie dies tun, bitte nicht erwarten, dass der Speicherplatz von besetzt besetzt jedes Objekt von der Java-Garbage-Collector, wenn das Objekt ist nicht mehr referenziert oder keine länger benötigt. Warum? Es ist nicht garantiert, dass die finalize() Methode wird die Ausführung in zeitnah Art und Weise abgeschlossen. Im schlimmsten Fall wird es möglicherweise nicht einmal aufgerufen, auch wenn keine Verweise auf das Objekt mehr vorhanden sind. Das bedeutet es ist nicht garantiert, dass alle Objekte , die eine finalize() Methode haben, Müll gesammelt werden.

Auch hat this article von Sun einige schöne Diagramme, die den Prozess erklären.

5

Eigentlich ist hier das Verhalten der Finalisierung() -Methode:

Sobald der Garbage Collector läuft (die VM entscheidet es um Speicher freizugeben muss, können Sie nicht zwingen, zu laufen) und beschlossen, den Speicher zu sammeln aus Dieses Objekt (das heißt, es gibt KEINE Referenzen, die von erreichbaren Objekten mehr auf es zeigen), kurz bevor es den von ihm belegten Speicher löscht, führt es die Methode finalize() für das Objekt aus. Sie können sicher sein, dass das Objekt finalize() ausgeführt wird, bevor es verschwindet, aber Sie können nicht sicher sein, dass es GC'ed wird, also sollten Sie sich nicht auf die Methode verlassen, um überhaupt eine Desinfizierung durchzuführen . Sie sollten in endlich {} Blöcken reinigende Anweisungen ausführen und nicht finalize() verwenden, da die Ausführung nicht garantiert ist.

Darüber hinaus haben einige Leute Leistungstests durchgeführt und gezeigt, dass die finalize-Methode die Erstellung/Zerstörung des Objekts etwas verlangsamt. Ich kann mich nicht an die Quelle erinnern, also behandle diese Information als nicht sehr zuverlässig. :)

+0

Finalizer verursachten Leistungsprobleme in NIO. Sie wurden in 1.4.1 (oder möglicherweise 1.4.2) entfernt. Wenn Sie NIO verwenden, wird von Ihnen erwartet, dass Sie "finally" richtig verwenden können. –

2

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()); 
    } 
}