Ich habe viele der Rookie-Java-Fragen gelesen finalize()
und finde es verwirrend, dass niemand wirklich deutlich gemacht hat, dass finalize () ein unzuverlässiger Weg ist, um Ressourcen zu bereinigen. Ich habe jemanden kommentieren sehen, dass er es verwendet, um Verbindungen zu bereinigen, was wirklich beängstigend ist, da der einzige Weg, um einer Garantie, dass eine Verbindung geschlossen ist, so nahe zu kommen, darin besteht, try (catch) endgültig zu implementieren.
Ich war nicht in CS geschult, aber ich programmiere seit fast einem Jahrzehnt professionell in Java und habe noch nie jemanden gesehen, der finalize()
jemals in ein Produktionssystem implementiert wurde. Dies bedeutet immer noch nicht, dass es nicht seinen Nutzen hat oder dass die Leute, mit denen ich zusammengearbeitet habe, es richtig gemacht haben.
Meine Frage ist also, welche Anwendungsfälle gibt es für die Implementierung finalize()
, die nicht zuverlässiger über einen anderen Prozess oder eine andere Syntax innerhalb der Sprache behandelt werden können?
Bitte geben Sie bestimmte Szenarien oder Ihre Erfahrungen an. Das einfache Wiederholen eines Java-Lehrbuchs oder die endgültige Verwendung ist nicht ausreichend, da dies nicht die Absicht dieser Frage ist.
finalize()
. Allerdings Plattform Bibliothekscode , wieSocketInputStream
, die im Namen des Anrufers nativen Ressourcen verwaltet, nicht so zu versuchen , das Risiko von Ressourcenlecks zu minimieren (oder verwendet gleichwertige Mechanismen, wie zum BeispielPhantomReference
, die später hinzugefügt wurden.) Also das Ökosystem sie braucht , obwohl 99,9999% der Entwickler niemals einen schreiben werden.Antworten:
Sie können es als Backstop für ein Objekt verwenden, das eine externe Ressource (Socket, Datei usw.) enthält. Implementieren Sie eine
close()
Methode und ein Dokument, die aufgerufen werden müssen.Implementieren
finalize()
Sie dieclose()
Verarbeitung, wenn Sie feststellen, dass sie nicht ausgeführt wurde. Vielleicht mit etwas,stderr
um darauf hinzuweisen, dass Sie nach einem fehlerhaften Anrufer aufräumen.Es bietet zusätzliche Sicherheit in einer außergewöhnlichen / fehlerhaften Situation. Nicht jeder Anrufer wird
try {} finally {}
jedes Mal die richtigen Dinge tun . Leider, aber in den meisten Umgebungen wahr.Ich bin damit einverstanden, dass es selten benötigt wird. Und wie Kommentatoren betonen, ist dies mit GC-Overhead verbunden. Verwenden Sie diese Option nur, wenn Sie die Sicherheit "Gürtel und Hosenträger" in einer lang laufenden App benötigen.
Ich sehe, dass ab Java 9
Object.finalize()
veraltet ist! Sie weisen uns aufjava.lang.ref.Cleaner
undjava.lang.ref.PhantomReference
als Alternativen.quelle
finalize()
ist ein Hinweis an die JVM, dass es hilfreich sein könnte, Ihren Code zu einem nicht festgelegten Zeitpunkt auszuführen. Dies ist gut, wenn Code auf mysteriöse Weise nicht ausgeführt werden soll.In Finalisierern ist es auch in drei Situationen gut, etwas Wichtiges zu tun (im Grunde alles außer Protokollierung):
Wenn Sie der Meinung sind, dass Sie finalize () benötigen, möchten Sie manchmal wirklich eine Phantomreferenz (die im angegebenen Beispiel einen harten Verweis auf eine von ihrem Verweis verwendete Verbindung enthalten und diese schließen kann, 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 zumindest keine Methoden für finalisierte Objekte aufrufen oder wiederbeleben kann. Es ist also genau richtig für Situationen, in denen Sie diese Verbindung nicht unbedingt sauber schließen müssen, dies aber gerne möchten und die Kunden Ihrer Klasse sich nicht selbst schließen können oder wollen (was eigentlich fair genug ist - Was' s der Punkt , an alle einen Garbage Collector zu haben , wenn Sie Schnittstellen entwickeln, erfordern eine bestimmte Aktion vor der Entnahme genommen werden? Das versetzt uns in die Zeit von malloc / free zurück.)
In anderen Fällen benötigen Sie die Ressource, die Sie für robuster halten. Warum müssen Sie beispielsweise 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 niedrigste Ressourcenebene bereitgestellt wird? Wenn der Server am anderen Ende unbedingt verlangt, dass Sie die Verbindung sauber schließen, anstatt nur den Socket fallen zu lassen, was passiert dann, wenn jemand über das Stromkabel des Computers stolpert, auf dem Ihr Code ausgeführt wird, oder wenn das dazwischenliegende Netzwerk ausfällt?
Haftungsausschluss: Ich habe in der Vergangenheit an einer JVM-Implementierung gearbeitet. Ich hasse Finalisierer.
quelle
finalize()
ohne Angabe von Gründen, die jemand wirklich nutzen möchte.java.lang.ref.Reference
und seine unterklassen. Java 1 hatte Variablen :-) Und ja, es hatte auch Finalizer.Eine einfache Regel: Verwenden Sie niemals Finalizer.
Die Tatsache allein, dass ein Objekt einen Finalizer hat (unabhängig davon, welchen Code es ausführt), reicht aus, um einen erheblichen Overhead für die Speicherbereinigung zu verursachen.
Aus einem Artikel von Brian Goetz:
quelle
Das einzige Mal, dass ich finalize im Produktionscode verwendet habe, war die Überprüfung, ob die Ressourcen eines bestimmten Objekts bereinigt wurden, und wenn nicht, eine sehr lautstarke Nachricht zu protokollieren. Es hat nicht wirklich versucht, es selbst zu tun, es hat nur viel geschrien, wenn es nicht richtig gemacht wurde. Es stellte sich als sehr nützlich heraus.
quelle
Ich mache Java seit 1998 professionell und habe es nie implementiert
finalize()
. Nicht einmal.quelle
Die akzeptierte Antwort ist gut. Ich wollte nur hinzufügen, dass es jetzt eine Möglichkeit gibt, die Funktionalität des Finalisierens zu nutzen, ohne sie überhaupt zu verwenden.
Schauen Sie sich die "Referenz" -Klassen an. Schwache Referenz, Phantomreferenz und weiche Referenz.
Sie können sie verwenden, um einen Verweis auf alle Ihre Objekte zu behalten, aber dieser Verweis ALLEIN beendet GC nicht. Das Schöne daran ist, dass Sie eine Methode aufrufen lassen können, wenn sie gelöscht wird, und dass diese Methode garantiert aufgerufen werden kann.
Zum Finalisieren: Ich habe finalize einmal verwendet, um zu verstehen, welche Objekte freigegeben wurden. Sie können einige nette Spiele mit Statik, Referenzzählung und dergleichen spielen - aber es war nur zur Analyse gedacht, aber achten Sie auf Code wie diesen (nicht nur beim Finalisieren, sondern dort, wo Sie ihn am wahrscheinlichsten sehen werden):
Es ist ein Zeichen dafür, dass jemand nicht wusste, was er tat. Ein solches "Aufräumen" ist praktisch nie erforderlich. Wenn die Klasse GC-fähig ist, erfolgt dies automatisch.
Wenn Sie einen solchen Code in einem Final finden, ist garantiert, dass die Person, die ihn geschrieben hat, verwirrt war.
Wenn es anderswo ist, kann es sein, dass der Code ein gültiger Patch für ein schlechtes Modell ist (eine Klasse bleibt lange bestehen und aus irgendeinem Grund mussten die Dinge, auf die sie verweist, manuell freigegeben werden, bevor das Objekt GC-fähig ist). Im Allgemeinen liegt es daran, dass jemand vergessen hat, einen Listener oder etwas zu entfernen, und nicht herausfinden kann, warum sein Objekt nicht GC-fähig ist. Deshalb löscht er einfach die Dinge, auf die er sich bezieht, und zuckt mit den Schultern und geht weg.
Es sollte niemals verwendet werden, um Dinge "schneller" aufzuräumen.
quelle
Ich bin mir nicht sicher, was Sie daraus machen können, aber ...
Ich denke, die Sonne hat einige Fälle gefunden, in denen sie verwendet werden sollte.
quelle
finalize
anstatt sie tatsächlich zu verwenden?$FAVORITE_IDE
.===================================
Ergebnis:
=====================================
Sie können also eine nicht erreichbare Instanz in der Finalisierungsmethode erreichbar machen.
quelle
System.gc();
keine Garantie dafür ist, dass der Garbage Collector tatsächlich ausgeführt wird. Es liegt im alleinigen Ermessen des GC, den Heap zu bereinigen (möglicherweise noch genügend freier Heap, möglicherweise nicht zu stark fragmentiert, ...). Es ist also nur eine bescheidene Bitte des Programmierers: "Bitte, könnten Sie mich anhören und rennen?" und kein Befehl "lauf jetzt wie ich sage!". Es tut uns leid, dass Sie linguale Wörter verwenden, aber es sagt genau, wie der GC der JVM funktioniert.finalize()
kann nützlich sein, um Ressourcenlecks zu erkennen. Wenn die Ressource geschlossen werden soll, aber nicht geschrieben wird, schreiben Sie die Tatsache, dass sie nicht geschlossen wurde, in eine Protokolldatei und schließen Sie sie. Auf diese Weise entfernen Sie das Ressourcenleck und können selbst feststellen, dass es aufgetreten ist, damit Sie es beheben können.Ich programmiere seit 1.0 alpha 3 (1995) in Java und muss noch finalize für irgendetwas überschreiben ...
quelle
Sie sollten sich nicht auf finalize () verlassen, um Ihre Ressourcen für Sie zu bereinigen. finalize () wird dann erst ausgeführt, wenn die Klasse durch Müll gesammelt wurde. Es ist viel besser, Ressourcen explizit freizugeben, wenn Sie sie nicht mehr verwenden.
quelle
Um einen Punkt in den obigen Antworten hervorzuheben: Finalizer werden auf dem einzelnen GC-Thread ausgeführt. Ich habe von einer großen Sun-Demo gehört, bei der die Entwickler einigen Finalisierern einen kleinen Schlaf verliehen und absichtlich eine ansonsten ausgefallene 3D-Demo in die Knie gezwungen haben.
Am besten vermeiden, mit möglicher Ausnahme der Test-Env-Diagnose.
Eckels Denken in Java hat einen guten Abschnitt dazu.
quelle
Hmmm, ich habe es einmal verwendet, um Objekte zu bereinigen, die nicht in einen vorhandenen Pool zurückgegeben wurden.
Sie wurden viel herumgereicht, so dass es unmöglich war zu sagen, wann sie sicher in den Pool zurückgebracht werden konnten. Das Problem war, dass es während der Speicherbereinigung eine enorme Strafe gab, die weitaus größer war als die Einsparungen durch das Zusammenlegen der Objekte. Es war ungefähr einen Monat in Produktion, bevor ich den ganzen Pool herausriss, alles dynamisch machte und damit fertig war.
quelle
Seien Sie vorsichtig mit dem, was Sie in einem tun
finalize()
. Vor allem, wenn Sie es beispielsweise für den Aufruf von close () verwenden, um sicherzustellen, dass die Ressourcen bereinigt werden. Wir hatten mehrere Situationen, in denen JNI-Bibliotheken mit dem laufenden Java-Code verknüpft waren, und unter allen Umständen, in denen wir finalize () zum Aufrufen von JNI-Methoden verwendet haben, kam es zu einer sehr schlechten Beschädigung des Java-Heaps. Die Beschädigung wurde nicht durch den zugrunde liegenden JNI-Code selbst verursacht, alle Speicherspuren in den nativen Bibliotheken waren in Ordnung. Es war nur die Tatsache, dass wir JNI-Methoden von finalize () überhaupt aufgerufen haben.Dies war mit einem JDK 1.5, das immer noch weit verbreitet ist.
Wir würden erst viel später herausfinden, dass etwas schief gelaufen ist, aber am Ende war der Schuldige immer die finalize () -Methode, die JNI-Aufrufe verwendete.
quelle
Beim Schreiben von Code, der von anderen Entwicklern verwendet wird, muss eine Art "Bereinigungs" -Methode aufgerufen werden, um Ressourcen freizugeben. Manchmal vergessen diese anderen Entwickler, Ihre Bereinigungsmethode (oder die Methode zum Schließen oder Zerstören oder was auch immer) aufzurufen. Um mögliche Ressourcenlecks zu vermeiden, können Sie die finalize-Methode überprüfen, um sicherzustellen, dass die Methode aufgerufen wurde. Wenn dies nicht der Fall ist, können Sie sie selbst aufrufen.
Viele Datenbanktreiber tun dies in ihren Statement- und Connection-Implementierungen, um ein wenig Sicherheit gegen Entwickler zu bieten, die vergessen haben, sie zu schließen.
quelle
Edit: Okay, es funktioniert wirklich nicht. Ich habe es implementiert und dachte, wenn es manchmal fehlschlägt, ist das für mich in Ordnung, aber es hat nicht einmal die Finalize-Methode ein einziges Mal aufgerufen.
Ich bin kein professioneller Programmierer, aber in meinem Programm habe ich einen Fall, den ich für ein gutes Beispiel für die Verwendung von finalize () halte, dh einen Cache, der seinen Inhalt auf die Festplatte schreibt, bevor er zerstört wird. Da es nicht notwendig ist, dass es jedes Mal bei Zerstörung ausgeführt wird, beschleunigt es nur mein Programm. Ich hoffe, dass ich es nicht falsch gemacht habe.
quelle
Es kann nützlich sein, Dinge zu entfernen, die einem globalen / statischen Ort hinzugefügt wurden (nicht erforderlich) und entfernt werden müssen, wenn das Objekt entfernt wird. Zum Beispiel:
quelle
this
im Konstruktor an ihn übergebene Instanz hat, diese Instanz niemals unerreichbar wird und daherfinalize()
ohnehin nie bereinigt wird. Wenn der Listener andererseits einen schwachen Verweis auf dieses Objekt hat, wie der Name schon sagt, besteht die Gefahr, dass dieses Konstrukt zu Zeiten bereinigt wird, in denen dies nicht der Fall sein sollte. Objektlebenszyklen sind nicht das richtige Werkzeug für die Implementierung der Anwendungssemantik.iirc - Sie können die Finalize-Methode verwenden, um einen Pooling-Mechanismus für teure Ressourcen zu implementieren. Sie erhalten also auch keine GCs.
quelle
Als Anmerkung:
Quelle: finalize () ist auf Java-9 veraltet
quelle
Die Ressourcen (Datei, Socket, Stream usw.) müssen geschlossen werden, sobald wir damit fertig sind. Sie haben im Allgemeinen eine
close()
Methode, die wir imfinally
Abschnitt vontry-catch
Anweisungen allgemein aufrufen . Manchmalfinalize()
kann es auch von wenigen Entwicklern verwendet werden, aber IMO ist kein geeigneter Weg, da es keine Garantie dafür gibt, dass finalize immer aufgerufen wird.In Java 7 haben wir eine Try-with-Resources- Anweisung, die wie folgt verwendet werden kann:
Im obigen Beispiel schließt try-with-resource die Ressource automatisch
BufferedReader
durch Aufrufen derclose()
Methode. Wenn wir möchten, können wir Closeable auch in unseren eigenen Klassen implementieren und auf ähnliche Weise verwenden. IMO scheint es ordentlicher und einfacher zu verstehen.quelle
Persönlich habe ich fast nie verwendet,
finalize()
außer in einem seltenen Fall: Ich habe eine benutzerdefinierte generische Sammlung erstellt und eine benutzerdefiniertefinalize()
Methode geschrieben, die Folgendes bewirkt:(
CompleteObject
Ist eine Schnittstelle ich gemacht, mit dem Sie festlegen , dass Sie nur selten implementierter implementiert habenObject
Methoden wie#finalize()
,#hashCode()
und#clone()
)Mit einer Schwestermethode
#setDestructivelyFinalizes(boolean)
kann das Programm, das meine Sammlung verwendet, (Hilfe) garantieren, dass durch das Zerstören eines Verweises auf diese Sammlung auch Verweise auf deren Inhalt zerstört werden und alle Fenster entfernt werden, die die JVM möglicherweise unbeabsichtigt am Leben erhalten. Ich überlegte auch, irgendwelche Fäden zu stoppen, aber das öffnete eine ganz neue Dose Würmer.quelle
finalize()
IhreCompleteObject
Instanzen wie im obigen Code aufrufen, wird sie möglicherweise zweimal aufgerufen, da die JVM möglicherweise immer noch aufgerufen wird,finalize()
sobald diese Objekte tatsächlich nicht mehr erreichbar sind. Dies liegt aufgrund der Logik der Finalisierung möglicherweise vor derfinalize()
Methode Ihrer speziellen Sammlung wird gleichzeitig oder sogar gleichzeitig angerufen. Da ich in Ihrer Methode keine Maßnahmen zur Gewährleistung der Thread-Sicherheit sehe, scheinen Sie sich dessen nicht bewusst zu sein ...Die akzeptierte Antwort listet auf, dass das Schließen einer Ressource während des Finalisierens erfolgen kann.
Diese Antwort zeigt jedoch, dass zumindest in Java8 mit dem JIT-Compiler unerwartete Probleme auftreten, bei denen manchmal der Finalizer aufgerufen wird, noch bevor Sie mit dem Lesen aus einem von Ihrem Objekt verwalteten Stream fertig sind.
Selbst in dieser Situation wäre es nicht empfehlenswert , finalize aufzurufen .
quelle