Warum ist die finalize-Methode in Java enthalten?


44

Nach diesem Beitrag sollten wir uns niemals auf die aufzurufende finalize-Methode verlassen. Warum hat Java es überhaupt in die Programmiersprache aufgenommen?

Es scheint eine schreckliche Entscheidung zu sein, in jede Programmiersprache eine Funktion aufzunehmen, die möglicherweise aufgerufen wird.

Antworten:


41

Laut Joshua Blochs Effective Java (Second Edition) gibt es zwei finalize()nützliche Szenarien :

  1. Eine Möglichkeit besteht darin, als "Sicherheitsnetz" zu fungieren, falls der Eigentümer eines Objekts vergisst, seine explizite Beendigungsmethode aufzurufen. Es gibt zwar keine Garantie dafür, dass der Finalizer umgehend aufgerufen wird, aber es ist möglicherweise besser, die Ressource zu spät als nie freizugeben, wenn der Client die explizite Beendigungsmethode nicht aufruft (hoffentlich selten). Der Finalizer sollte jedoch eine Warnung protokollieren, wenn er feststellt, dass die Ressource nicht beendet wurde

  2. Eine zweite legitime Verwendung von Finalizern betrifft Objekte mit einheimischen Kollegen. Ein nativer Peer ist ein natives Objekt, an das ein normales Objekt über native Methoden delegiert. Da ein nativer Peer kein normales Objekt ist, weiß der Garbage Collector nichts davon und kann es nicht zurückfordern, wenn sein Java-Peer zurückgefordert wird. Ein Finalizer ist ein geeignetes Mittel, um diese Aufgabe auszuführen, vorausgesetzt, der native Peer verfügt über keine kritischen Ressourcen. Wenn der native Peer Ressourcen enthält, die sofort beendet werden müssen, sollte die Klasse über eine explizite Beendigungsmethode verfügen, wie oben beschrieben. Die Beendigungsmethode sollte alle erforderlichen Schritte ausführen, um die kritische Ressource freizugeben.

Weitere Informationen finden Sie unter Punkt 7, Seite 27.


4
Mit # 2 klingt es so, als ob der native Peer eine native Ressource ist, die ein Sicherheitsnetz benötigt und eine Beendigungsmethode haben sollte und daher kein separater Punkt sein sollte.
Mooing Duck

2
@MooingDuck: # 2 ist ein separater Punkt, da keine explizite Beendigungsmethode verfügbar gemacht werden muss, wenn der native Peer keine kritischen Ressourcen enthält (z. B. nur ein speicherinternes Objekt). Bei dem Sicherheitsnetz von Nummer 1 geht es ausdrücklich um eine Kündigungsmethode.
Jhominal

55

Finalizer sind wichtig für die Verwaltung nativer Ressourcen. Beispielsweise muss Ihr Objekt möglicherweise ein WidgetHandle vom Betriebssystem mithilfe einer Nicht-Java-API zuweisen. Wenn Sie dieses WidgetHandle nicht freigeben, während Ihr Objekt mit GCs versehen ist, werden Sie WidgetHandles verlieren.

Wichtig ist, dass die Fälle, in denen "Finalizer heißt nie", eher einfach aufgeschlüsselt werden:

  1. Das Programm wird schnell beendet
  2. Das Objekt "lebt für immer" während der Laufzeit des Programms
  3. Der Computer schaltet sich aus / Ihr Prozess wird vom Betriebssystem beendet / etc

In allen drei Fällen haben Sie entweder kein systemeigenes Leck (aufgrund der Tatsache, dass Ihr Programm nicht mehr ausgeführt wird) oder Sie haben bereits ein nicht systemeigenes Leck (wenn Sie verwaltete Objekte weiterhin zuweisen, ohne dass dies der Fall ist) GC'd).

Bei der Warnung "Verlassen Sie sich nicht darauf, dass der Finalizer aufgerufen wird" geht es wirklich darum, Finalizer nicht für die Programmlogik zu verwenden. Sie möchten beispielsweise nicht verfolgen, wie viele Ihrer Objekte in allen Instanzen Ihres Programms vorhanden sind, indem Sie einen Zähler in einer Datei während der Erstellung inkrementieren und in einem Finalizer dekrementieren - da keine Garantie dafür besteht, dass Ihre Objekte vorhanden sind abgeschlossen sein, wird dieser Dateizähler wahrscheinlich nie auf 0 zurückgehen. Dies ist wirklich ein Sonderfall des allgemeineren Prinzips, dass Sie nicht davon abhängen sollten, dass Ihr Programm normal beendet wird (Stromausfälle usw.).

Bei der Verwaltung nativer Ressourcen entsprechen die Fälle, in denen der Finalizer nicht ausgeführt wird, denen, die Sie nicht interessieren, wenn sie nicht ausgeführt werden.


Hervorragende Antwort. Ich war wie selbst wenn es genannt werden könnte .. es sollte immer noch enthalten sein. Ich war eine Weile verwirrt mit der Frage und der verknüpften auf StackOverflow.
InformedA

3
Es kam nicht in Frage, aber es lohnt sich, im Zusammenhang mit "Verlassen Sie sich nicht darauf, dass der Finalizer aufgerufen wird" zu diskutieren: Der Finalizer kann aufgerufen werden, jedoch nur nach einer willkürlich langen Verzögerung, nachdem das Objekt nicht mehr erreichbar ist. Dies ist wichtig für "native Ressourcen", die weitaus knapper sind als der Arbeitsspeicher (was die meisten davon sind, stellt sich heraus).

26
"Finalizer sind wichtig für die Verwaltung von nativen Ressourcen." - Nein, tut mir leid. Finalizer für die Verwaltung nativer Ressourcen zu verwenden, ist eine schreckliche Idee (weshalb wir jetzt autocloseable und co haben). Der GC kümmert sich nur um den Speicher, nicht um andere Ressourcen. Dies bedeutet, dass Sie keine nativen Ressourcen mehr haben, aber dennoch genug Speicher haben, um keine GC auszulösen - das wollen wir wirklich nicht. Außerdem verteuern Finalizer die GC, was ebenfalls keine gute Idee ist.
Voo

12
Ich bin damit einverstanden, dass Sie weiterhin eine explizite native Ressourcenverwaltungsstrategie haben sollten, die sich von den Finalisierern unterscheidet. Wenn Ihr Objekt sie jedoch zuweist und sie im Finalisierer nicht freigibt, öffnen Sie sich in dem Fall, in dem sich das Objekt befindet, einem nativen Leck GC'd ohne die explizite Freigabe der Ressource. Mit anderen Worten, Finalizer sind ein wichtiger Bestandteil einer systemeigenen Ressourcenmanagementstrategie und keine Lösung des Problems im Allgemeinen.
Ryan Cavanaugh

1
@Voo es ist wahrscheinlich richtiger zu sagen, dass der Finalizer der Fallback ist, falls der Vertrag des Objekts nicht eingehalten wird ( close()wird nie aufgerufen, bevor er nicht mehr erreichbar ist)
Ratschenfreak

5

Der Zweck dieser Methode wird in der API-Dokumentation wie folgt erläutert :

Es wird aufgerufen, wenn die Java Virtual Machine festgestellt hat, dass kein Thread mehr auf dieses Objekt zugreifen kann, der noch nicht abgestorben ist, es sei denn, es handelt sich um eine Aktion, die durch die Finalisierung eines anderen Objekts ausgeführt wurde oder eine Klasse, die fertiggestellt werden kann ...

Der übliche Zweck von finalize... besteht darin , Bereinigungsaktionen durchzuführen, bevor das Objekt endgültig gelöscht wird . Die Methode finalize für ein Objekt, das eine Eingabe- / Ausgabeverbindung darstellt, kann beispielsweise explizite E / A-Transaktionen ausführen, um die Verbindung zu trennen, bevor das Objekt endgültig gelöscht wird.


Wenn Sie sich außerdem für Gründe interessieren, aus denen Sprachentwickler entschieden haben, dass "Objekte unwiderruflich verworfen werden" ( gesammelter Müll ), die sich der Kontrolle des Anwendungsprogrammierers entziehen ("wir sollten uns niemals verlassen"), wurde dies in einer Antwort auf "related " erläutert frage :

Automatische Speicherbereinigung ... beseitigt ganze Klassen von Programmierfehlern, die C- und C ++ - Programmierer stören. Sie können Java-Code mit der Gewissheit entwickeln, dass das System viele Fehler schnell findet und größere Probleme erst nach Auslieferung des Produktionscodes behoben werden.

Das obige Zitat stammt wiederum aus der offiziellen Dokumentation über Java-Entwurfsziele , dh es kann als maßgebliche Referenz angesehen werden, die erklärt, warum sich Java-Sprachdesigner für diesen Weg entschieden haben.

Eine ausführlichere und sprachunabhängigere Beschreibung dieser Einstellung finden Sie in OOSC, Abschnitt 9.6, Automatische Speicherverwaltung . Dieser Abschnitt beginnt mit einer eindeutigen Aussage:

Eine gute OO-Umgebung sollte einen automatischen Speicherverwaltungsmechanismus bieten, mit dem nicht erreichbare Objekte erkannt und wiederhergestellt werden können, sodass sich Anwendungsentwickler auf die Entwicklung ihrer Jobanwendungen konzentrieren können.

Die vorangegangene Diskussion sollte ausreichen, um zu zeigen, wie wichtig es ist, eine solche Einrichtung zur Verfügung zu haben. Mit den Worten von Michael Schweitzer und Lambert Strether:

Ein objektorientiertes Programm ohne automatische Speicherverwaltung ist in etwa das gleiche wie ein Schnellkochtopf ohne Sicherheitsventil: Früher oder später wird das Ding sicher in die Luft jagen!


4

Finalizer gibt es, weil von ihnen erwartet wurde, dass sie ein wirksames Mittel sind, um sicherzustellen, dass die Dinge aufgeräumt werden (auch wenn dies in der Praxis nicht der Fall ist), und weil sie bei ihrer Erfindung ein besseres Mittel sind, um die Aufräumarbeiten sicherzustellen (wie Phantomreferenzen und Versuchsanleitungen) -ressourcen) gab es noch nicht. Im Nachhinein wäre Java wahrscheinlich besser, wenn der Aufwand für die Implementierung der "Finalize" -Funktion für andere Bereinigungsmaßnahmen aufgewendet worden wäre, was jedoch während der anfänglichen Entwicklung von Java kaum klar war.


3

CAVEAT: Ich bin vielleicht nicht mehr auf dem neuesten Stand, aber so verstehe ich es seit ein paar Jahren:

Im Allgemeinen kann nicht garantiert werden, wann ein Finalizer ausgeführt wird - oder ob er überhaupt ausgeführt wird. Bei einigen JVMs können Sie jedoch eine vollständige GC und Finalisierung anfordern, bevor das Programm beendet wird (was natürlich bedeutet, dass das Programm länger dauert) zu beenden, und das ist nicht die Standard-Betriebsart).

Und es war bekannt, dass einige GCs das GC'ing von Objekten mit Finalisierern explizit verzögern oder vermeiden, in der Hoffnung, dass dies zu einer besseren Leistung bei Benchmarks führen würde.

Diese Verhaltensweisen stehen leider im Widerspruch zu den ursprünglichen Gründen, aus denen Finalizer empfohlen wurden, und haben stattdessen die Verwendung explizit genannter Abschaltmethoden gefördert.

Wenn Sie ein Objekt haben, das wirklich bereinigt werden muss, bevor es verworfen wird, und Sie den Benutzern nicht wirklich vertrauen können, ist ein Finalizer möglicherweise noch eine Überlegung wert. Aber im Allgemeinen gibt es gute Gründe, warum Sie sie im modernen Java-Code nicht so oft sehen wie in einigen der frühen Beispiele.


1

Der Google Java Style Guide enthält einige Ratschläge zu diesem Thema:

Es ist äußerst selten zu überschreiben Object.finalize.

Tipp : Tu es nicht. Wenn Sie dies unbedingt tun müssen, lesen und verstehen Sie zuerst Effective Java Item 7, "Vermeiden Sie Finalizer", sehr sorgfältig und tun Sie es dann nicht.


5
Warum ich die übermäßige Vereinfachung des Problems bewundere, ist keine Antwort auf die Frage, warum es existiert.
Pierre Arlaud

1
Ich glaube, ich habe es in meiner Antwort nicht gut formuliert, aber (IMO) die Antwort auf "Warum existiert es?" ist "es sollte nicht". In den seltenen Fällen, in denen Sie eine Finalisierungsoperation wirklich benötigen, möchten Sie diese wahrscheinlich selbst mit PhantomReferenceund erstellen ReferenceQueue.
Daniel Pryden

Ich meinte, während * offensichtlich, obwohl ich dich nicht abgelehnt habe. Die am besten bewertete Antwort zeigt jedoch, wie nützlich die Methode ist, und macht Ihren Standpunkt irgendwie ungültig.
Pierre Arlaud

1
Sogar im Fall von Peers halte ich dies für PhantomReferenceeine bessere Lösung. Finalizer sind eine Warze, die aus den Anfängen von Java übrig geblieben ist. Gleiche Object.clone()und rohe Typen gehören zu den am besten vergessenen Sprachen.
Daniel Pryden

1
Die Gefahren von Finalisierern werden in C # besser zugänglich (für Entwickler verständlich) beschrieben. Man darf jedoch nicht implizieren, dass die zugrunde liegenden Mechanismen auf Java und C # gleich sind. Es gibt nichts in ihren Spezifikationen, was dies aussagt.
Rwong

1

In der Java-Sprachspezifikation (Java SE 7) heißt es:

Finalizer bieten die Möglichkeit, Ressourcen freizugeben, die von einem automatischen Speichermanager nicht automatisch freigegeben werden können. In solchen Situationen kann durch einfaches Zurückfordern des von einem Objekt verwendeten Speichers nicht garantiert werden, dass die darin enthaltenen Ressourcen zurückgefordert werden.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.