Warum würdest du jemals finalize () implementieren?

Ich habe viele der Rookie-Java-Fragen über finalize () durchgelesen und finde es verwirrend, dass niemand wirklich deutlich gemacht hat, dass finalize () eine unzuverlässige Möglichkeit ist, Ressourcen aufzuräumen. Ich habe jemanden kommentiert, der sie benutzt, um Connections zu bereinigen, was wirklich beängstigend ist, da die einzige Möglichkeit, der Garantie, dass eine Connection geschlossen ist, so nahe kommt, ist, try (catch) endlich zu implementieren.

Ich war nicht in CS geschult, aber ich programmiere seit fast einem Jahrzehnt professionell in Java und ich habe noch nie jemanden gesehen, finalize () in einem Produktionssystem zu implementieren. Dies bedeutet immer noch nicht, dass es seine Verwendung nicht hat, oder dass Leute, mit denen ich gearbeitet habe, es richtig gemacht haben.

Meine Frage ist also, welche Anwendungsfälle gibt es für die Implementierung von finalize (), die nicht zuverlässiger über einen anderen process oder eine andere Syntax innerhalb der Sprache gehandhabt werden können?

Bitte stellen Sie spezifische Szenarien oder Ihre Erfahrung zur Verfügung, indem Sie einfach ein Java-Lehrbuch wiederholen oder den beabsichtigten Verwendungszweck finalisieren. Dies ist nicht die Absicht dieser Frage.

   

Sie könnten es als Backstop für ein Objekt verwenden, das eine externe Ressource (Socket, Datei usw.) enthält. Implementieren Sie eine close() -Methode und dokumentieren Sie, dass sie aufgerufen werden muss.

Implementieren Sie finalize() , um die close() Verarbeitung durchzuführen, wenn Sie feststellen, dass dies nicht erfolgt ist. Vielleicht mit etwas, das du an stderr ausgegeben hast, um darauf hinzuweisen, dass du nach einem errorshaften Anrufer aufräumst.

Es bietet zusätzliche Sicherheit in einer außergewöhnlichen / Buggy Situation. Nicht jeder Aufrufer wird jedesmal das richtige try {} finally {} Zeug try {} finally {} . Unglücklich, aber in den meisten Umgebungen wahr.

Ich stimme zu, dass es nur selten benötigt wird. Und wie Kommentatoren darauf hinweisen, kommt es mit GC Overhead. Nur verwenden, wenn Sie diese “Gürtel und Hosenträger” Sicherheit in einer lang laufenden App brauchen.

Ich sehe, dass Object.finalize() Java 9 veraltet ist! Sie verweisen uns als Alternativen auf java.lang.ref.Cleaner und java.lang.ref.PhantomReference .

finalize () ist ein Hinweis für die JVM, dass es sinnvoll ist, den Code zu einem nicht spezifizierten Zeitpunkt auszuführen. Das ist gut, wenn Code auf mysteriöse Weise nicht ausgeführt werden soll.

Etwas Wichtiges in Finalizern zu tun (grundsätzlich alles außer Logging) ist auch in drei Situationen gut:

  • Sie möchten spielen, dass andere finalisierte Objekte immer noch in einem Zustand sind, den der Rest Ihres Programms für gültig hält.
  • Sie möchten allen Methoden Ihrer classn, die über einen Finalizer verfügen, viel Prüfcode hinzufügen, um sicherzustellen, dass sie sich nach der Finalisierung korrekt verhalten.
  • Sie möchten versehentlich finalisierte Objekte wiederbeleben und viel Zeit damit verbringen herauszufinden, warum sie nicht funktionieren und / oder warum sie nicht finalisiert werden, wenn sie schließlich veröffentlicht werden.

Wenn Sie denken, dass Sie finalize () benötigen, ist es manchmal eine Phantomreferenz (die im Beispiel eine feste Referenz auf eine Verbindung enthalten könnte, die von ihrem Verweis verwendet wird, und schließen, nachdem die Phantomreferenz in die Warteschlange gestellt wurde). Dies hat auch die Eigenschaft, dass es auf mysteriöse Weise niemals ausgeführt werden kann, aber es kann zumindest keine Methoden aufrufen oder finalisierte Objekte wiederbeleben. Es ist also genau richtig für Situationen, in denen du diese Verbindung nicht unbedingt sauber schließen musst, aber du würdest es gerne tun, und die Klienten deiner class können oder wollen sich nicht selbst schließen (was eigentlich fair ist – Was nützt es überhaupt, einen Garbage Collector zu haben, wenn Sie Interfaces entcasting, die vor der Erfassung einer bestimmten Aktion bedürfen ? Das versetzt uns zurück in die Zeit von malloc / free.

In anderen Fällen benötigen Sie die Ressource, die Ihrer Meinung nach robuster ist. Zum Beispiel, warum müssen Sie diese Verbindung schließen? Es muss letztendlich auf einer Art von E / A basieren, die vom System bereitgestellt wird (Socket, Datei, was auch immer). Warum können Sie sich also nicht darauf verlassen, dass das System es für Sie schließt, wenn die unterste Ebene der Ressource gced ist? Wenn der Server am anderen Ende unbedingt verlangt, dass Sie die Verbindung sauber schließen und nicht einfach den Socket fallen lassen, was passiert dann, wenn jemand über das Stromkabel des Computers stolpert, auf dem Ihr Code läuft oder das intervenierende Netzwerk ausfällt?

Haftungsausschluss: Ich habe in der Vergangenheit an einer JVM-Implementierung gearbeitet. Ich hasse Finalizer.

Eine einfache Regel: Benutze niemals Finalizer. Allein die Tatsache, dass ein Objekt einen Finalizer hat (unabhängig davon, welcher Code ausgeführt wird) reicht aus, um einen beträchtlichen Overhead für die Garbage Collection zu verursachen.

Aus einem Artikel von Brian Goetz:

Objekte mit Finalizern (die eine nicht-triviale finalize () -Methode haben) haben einen erheblichen Mehraufwand im Vergleich zu Objekten ohne Finalizer und sollten sparsam verwendet werden. Abschließbare Objekte sind langsamer zuzuweisen und langsamer zu sammeln. Zur Zuweisungszeit muss die JVM alle finalisierbaren Objekte mit dem Garbage Collector registrieren, und (zumindest in der HotSpot JVM-Implementierung) müssen finalisierbare Objekte einem langsameren Zuweisungspfad als die meisten anderen Objekte folgen. Ebenso sind finalisierbare Objekte auch langsamer zu sammeln. Es dauert mindestens zwei Speicherbereinigungszyklen (im besten Fall), bevor ein abschließbares Objekt zurückgewonnen werden kann, und der Speicherbereiniger muss zusätzliche Arbeit leisten, um den Finalizer aufzurufen. Das Ergebnis ist mehr Zeit für das Zuweisen und Sammeln von Objekten und mehr Druck auf den Garbage Collector, da der von nicht erreichbaren finalisierbaren Objekten verwendete Speicher länger beibehalten wird. Kombinieren Sie dies mit der Tatsache, dass Finalizer nicht garantiert werden, in irgendeinem vorhersehbaren Zeitrahmen oder sogar überhaupt zu laufen, und Sie können sehen, dass es relativ wenige Situationen gibt, für die Finalisierung das richtige Werkzeug ist.

Die einzige Zeit, die ich im Produktionscode finalisiert habe, war eine Überprüfung, ob die Ressourcen eines gegebenen Objekts bereinigt wurden, und wenn nicht, logge eine sehr laute Nachricht ein. Es hat nicht wirklich versucht und es selbst gemacht, es hat nur viel geschrien, wenn es nicht richtig gemacht wurde. Hat sich als sehr nützlich erwiesen.

Ich mache Java seit 1998 professionell und habe finalize () nie implementiert. Nicht einmal.

Ich bin mir nicht sicher, was Sie daraus machen können, aber …

 itsadok@laptop ~/jdk1.6.0_02/src/ $ find . -name "*.java" | xargs grep "void finalize()" | wc -l 41 

Also denke ich, dass die Sonne einige Fälle gefunden hat, wo (sie denken) sie benutzt werden sollte.

Ich habe es einmal abgeschlossen, um zu verstehen, welche Objekte freigegeben wurden. Sie können einige nette Spiele mit Statik, Referenzzählung und so spielen – aber es war nur für die Analyse.

Die angenommene Antwort ist gut, ich wollte nur hinzufügen, dass es jetzt einen Weg gibt, die functionalität von finalize zu haben, ohne sie überhaupt zu benutzen.

Sehen Sie sich die “Referenz” -classn an. Schwache Referenz usw.

Sie können sie verwenden, um einen Verweis auf alle Ihre Objekte zu behalten, aber diese Referenz ALONE stoppt GC nicht. Das Schöne dabei ist, dass Sie eine Methode aufrufen können, wenn sie gelöscht wird, und diese Methode kann garantiert aufgerufen werden.

Eine andere Sache, auf die man achten sollte. Jedes Mal, wenn Sie irgendwo im Code etwas Ähnliches sehen (nicht nur in finalize, aber dort sehen Sie es am ehesten):

 public void finalize() { ref1 = null; ref2 = null; othercrap = null; } 

Es ist ein Zeichen, dass jemand nicht wusste, was sie taten. “Aufräumen” wird praktisch nie benötigt. Wenn die class GC’d ist, wird dies automatisch durchgeführt.

Wenn Sie Code wie diesen in einem finalize finden, ist es garantiert, dass die Person, die es schrieb, verwirrt war.

Wenn es anderswo ist, könnte es sein, dass der Code ein gültiger Patch für ein schlechtes Modell ist (eine class bleibt für eine lange Zeit bestehen und aus irgendeinem Grund mussten Dinge, auf die sie verwiesen wurde, manuell freigegeben werden, bevor das Objekt gecodiert wurde). Im Allgemeinen liegt es daran, dass jemand vergessen hat, einen Zuhörer oder etwas zu entfernen, und nicht herausfinden kann, warum sein Objekt nicht gecodiert wird, also löschen sie einfach Dinge, auf die es sich bezieht und zucken mit den Schultern und gehen weg.

Es sollte nie verwendet werden, um Dinge “schneller” zu reinigen.

 class MyObject { Test main; public MyObject(Test t) { main = t; } protected void finalize() { main.ref = this; // let instance become reachable again System.out.println("This is finalize"); //test finalize run only once } } class Test { MyObject ref; public static void main(String[] args) { Test test = new Test(); test.ref = new MyObject(test); test.ref = null; //MyObject become unreachable,finalize will be invoked System.gc(); if (test.ref != null) System.out.println("MyObject still alive!"); } } 

===================================

Ergebnis:

Dies ist abgeschlossen

MyObject lebt noch!

========================================

Sie können also eine nicht erreichbare Instanz in Finalize-Methode erreichen.

finalize kann nützlich sein, um Ressourcenlecks zu erfassen. Wenn die Ressource geschlossen werden soll aber nicht die Tatsache schreiben, dass sie nicht zu einer Protokolldatei geschlossen wurde und sie schließen. Auf diese Weise entfernen Sie das Ressourcenleck und geben sich selbst einen Weg zu erkennen, dass es passiert ist, damit Sie es beheben können.

Ich habe in Java seit 1.0 Alpha 3 (1995) programmiert und ich habe noch für alles finalize überschreiben …

Sie sollten sich nicht auf finalize () verlassen, um Ihre Ressourcen für Sie aufzuräumen. finalize () wird erst dann ausgeführt, wenn die class Garbage Collected ist. Es ist viel besser, Ressourcen explizit freizugeben, wenn Sie sie nicht mehr verwenden.

Um einen Punkt in den obigen Antworten zu markieren: Finalizer werden auf dem einzelnen GC-Thread ausgeführt. Ich habe von einer großen Sun-Demo gehört, bei der die Entwickler einigen Finalern einen kleinen Schlaf hinzufügten und absichtlich ein ansonsten ausgefallenes 3D-Demo in die Knie zwangen.

Am besten zu vermeiden, mit Ausnahme der Test-env-Diagnose.

Eckels Thinking in Java hat dazu einen guten Teil .

Beim Schreiben von Code, der von anderen Entwicklern verwendet wird, die eine Art “Bereinigungs” -Methode erfordern, die aufgerufen wird, um Ressourcen freizugeben. Manchmal vergessen diese anderen Entwickler, Ihre Bereinigungsmethode aufzurufen (oder zu schließen oder zu zerstören). Um mögliche Ressourcenlecks zu vermeiden, können Sie die Finalize-Methode einchecken, um sicherzustellen, dass die Methode aufgerufen wurde, und falls nicht, können Sie sie selbst aufrufen.

Viele databasetreiber tun dies in ihren Statement- und Connection-Implementierungen, um ein wenig Sicherheit gegenüber Entwicklern zu bieten, die vergessen, auf sie zu schließen.

Hmmm, ich habe es einmal verwendet, um Objekte zu bereinigen, die nicht in einen vorhandenen Pool zurückgegeben wurden. Sie wurden viel herumgereicht, daher war es unmöglich zu sagen, wann sie sicher in den Pool zurückkehren konnten. Das Problem war, dass es während der Garbage-Collection zu einer großen Strafe kam, die weit größer war als alle Einsparungen durch das Poolen der Objekte. Es wurde ungefähr einen Monat lang produziert, bevor ich den ganzen Pool herausriss, alles dynamisch machte und damit fertig war.

Edit: Okay, es funktioniert wirklich nicht. Ich habe es implementiert und dachte, wenn es schief geht, ist das für mich in Ordnung, aber es hat die Finalize-Methode nicht einmal ein einziges Mal aufgerufen.

Ich bin kein professioneller Programmierer, aber in meinem Programm habe ich einen Fall, den ich denke, um ein Beispiel für einen guten Fall der Verwendung von finalize (), das ist ein Cache, der seinen Inhalt auf die Festplatte schreibt, bevor es zerstört wird. Weil es nicht notwendig ist, dass es jedes Mal auf der Zerstörung ausgeführt wird, beschleunigt es nur mein Programm, ich hoffe, dass ich es nicht falsch gemacht habe.

 @Override public void finalize() { try {saveCache();} catch (Exception e) {e.printStackTrace();} } public void saveCache() throws FileNotFoundException, IOException { ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("temp/cache.tmp")); out.writeObject(cache); } 

Es kann praktisch sein, Dinge zu entfernen, die zu einem globalen / statischen Ort hinzugefügt wurden (aus Not geraten) und entfernt werden müssen, wenn das Objekt entfernt wird. Zum Beispiel:

     private void addGlobalClickListener () {
         weakAwtEventListener = new SchwachAWTEventListener (this);

         Toolkit.getDefaultToolkit (). AddAWTEventListener (weakAwtEventListener, AWTEvent.MOUSE_EVENT_MASK);
     }

     @Override
     protected void finalize () wirft Throwable {
         super.finalize ();

         if (weakAwtEventListener! = null) {
             Toolkit.getDefaultToolkit (). RemoveAWTEventListener (weakAwtEventListener);
         }
     }

Sei vorsichtig, was du in einem finalize () machst. Vor allem, wenn Sie es für Dinge wie den Aufruf von close () verwenden, um sicherzustellen, dass Ressourcen bereinigt werden. Wir stießen auf verschiedene Situationen, in denen JNI-Bibliotheken mit dem laufenden Java-Code verknüpft waren. Unter allen Umständen, bei denen wir finalize () zum Aufrufen von JNI-Methoden verwendeten, erhielten wir eine sehr schlechte Java-Heap-Korruption. Die Beschädigung wurde nicht durch den zugrunde liegenden JNI-Code selbst verursacht, alle Speicherspuren waren in den nativen Bibliotheken in Ordnung. Es war nur die Tatsache, dass wir überhaupt JNI-Methoden von finalize () anriefen.

Dies war mit einem JDK 1.5, das immer noch weit verbreitet ist.

Wir würden nicht herausfinden, dass etwas viel später schief ging, aber am Ende war der Schuldige immer die finalize () Methode, die JNI Aufrufe verwendet.

iirc – Sie können die Finalize-Methode verwenden, um einen Pooling-Mechanismus für teure Ressourcen zu implementieren – damit sie auch keine GCs erhalten.

Die akzeptierte Antwort listet auf, dass das Schließen einer Ressource während des Finalisierens durchgeführt werden kann.

Diese Antwort zeigt jedoch, dass es zumindest in Java8 mit dem JIT-Compiler zu unerwarteten Problemen kommt, bei denen manchmal der Finalizer aufgerufen wird, noch bevor Sie das Lesen aus einem von Ihrem Objekt verwalteten Stream abgeschlossen haben.

Selbst in dieser Situation wäre das Anrufen von finalize nicht zu empfehlen.

Persönlich verwende ich finalize() fast nie, außer in einem seltenen Fall: Ich habe eine benutzerdefinierte generische Sammlung erstellt und eine benutzerdefinierte finalize() -Methode geschrieben, die Folgendes ausführt:

 public void finalize() throws Throwable { super.finalize(); if (destructiveFinalize) { T item; for (int i = 0, l = length(); i < l; i++) { item = get(i); if (item == null) { continue; } if (item instanceof Window) { ((Window) get(i)).dispose(); } if (item instanceof CompleteObject) { ((CompleteObject) get(i)).finalize(); } set(i, null); } } } 

( CompleteObject ist eine von mir erstellte Schnittstelle, mit der Sie angeben können, dass Sie selten implementierte Object Methoden wie #finalize() , #hashCode() und #clone() )

Mit einer Schwestermethode #setDestructivelyFinalizes(boolean) kann das Programm, das meine Sammlung verwendet, also sicherstellen, dass das Zerstören eines Verweises auf diese Sammlung auch Verweise auf seinen Inhalt zerstört und alle Fenster, die die JVM unbeabsichtigt am Leben halten könnten, deaktiviert. Ich überlegte auch, ob ich irgendwelche Threads stoppen sollte, aber das eröffnete eine ganz neue Dose Würmer.

Die Ressourcen (Datei, Socket, Stream usw.) müssen geschlossen werden, sobald wir mit ihrer Verwendung fertig sind. Sie haben im Allgemeinen eine close() -Methode, die wir im Allgemeinen finally Abschnitt von try-catch statementen bezeichnen. Manchmal kann finalize() auch von wenigen Entwicklern verwendet werden, aber IMO ist kein geeigneter Weg, da es keine Garantie gibt, dass finalize immer aufgerufen wird.

In Java 7 haben wir eine try-with-resources- statement, die wie folgt verwendet werden kann:

 try (BufferedReader br = new BufferedReader(new FileReader(path))) { // Processing and other logic here. } catch (Exception e) { // log exception } finally { // Just in case we need to do some stuff here. } 

Im obigen Beispiel schließt try-with-resource automatisch die Ressource BufferedReader durch Aufruf der Methode close() . Wenn wir möchten, können wir Closeable in unseren eigenen classn implementieren und auf ähnliche Weise verwenden. IMO scheint es einfacher und einfacher zu verstehen.

Als Anmerkung:

Ein Objekt, das finalize () überschreibt, wird speziell vom Garbage Collector behandelt. Normalerweise wird ein Objekt sofort während des Erfassungszyklus zerstört, nachdem das Objekt nicht mehr im Geltungsbereich ist. Finalisierbare Objekte werden jedoch stattdessen in eine Warteschlange verschoben, in der separate Finalisierungsthreads die Warteschlange entleeren und die Methode finalize () für jedes Objekt ausführen. Sobald die finalize () -Methode beendet ist, wird das Objekt im nächsten Zyklus endlich für die Garbage-Collection bereit sein.

finalisieren Sie veraltet auf Java-9