Wann wird die Methode finalize () in Java aufgerufen?


330

Ich muss wissen, wann die finalize()Methode in der aufgerufen wird JVM. Ich habe eine Testklasse erstellt, die beim Aufrufen der finalize()Methode in eine Datei schreibt, indem sie überschrieben wird. Es wird nicht ausgeführt. Kann mir jemand den Grund sagen, warum es nicht ausgeführt wird?


33
Nur als Randnotiz: Finalize ist in Java 9
Reg


wenn irgendetwas einen Verweis auf Ihr Objekt oder sogar die Klasse hat. finalize()und die Speicherbereinigung hat keine Auswirkungen.
ha9u63ar

Antworten:


273

Im Allgemeinen ist es am besten, sich nicht auf finalize()Aufräumarbeiten usw. zu verlassen .

Laut Javadoc (was es wert wäre, gelesen zu werden) ist es:

Wird vom Garbage Collector für ein Objekt aufgerufen, wenn die Garbage Collection feststellt, dass keine Verweise mehr auf das Objekt vorhanden sind.

Wie Joachim betonte, kann dies im Leben eines Programms niemals passieren, wenn das Objekt immer zugänglich ist.

Außerdem kann nicht garantiert werden, dass der Garbage Collector zu einem bestimmten Zeitpunkt ausgeführt wird. Im Allgemeinen versuche ich zu sagen, dass dies finalize()wahrscheinlich nicht die beste Methode im Allgemeinen ist, es sei denn, Sie benötigen etwas Bestimmtes.


19
Mit anderen Worten (nur um dies für zukünftige Leser zu verdeutlichen), es wird niemals für die Hauptklasse aufgerufen, da beim Schließen der Hauptklasse kein Müll gesammelt werden muss. Das Betriebssystem bereinigt sowieso alles, was die App verwendet.
Mark Jeronimus

113
"Nicht die beste Methode ... es sei denn, es gibt etwas Bestimmtes, für das Sie es benötigen" - ähm, dieser Satz gilt für 100% von allem, ist also nicht hilfreich. Joachim Sauers Antwort ist viel besser
BT

1
@ Zom-B Ihr Beispiel ist hilfreich zur Verdeutlichung, aber nur um pedantisch zu sein, könnte es vermutlich für die Hauptklasse aufgerufen werden, wenn die Hauptklasse einen Nicht-Daemon-Thread erstellt und dann zurückgibt?
Tom G

3
In welchen Situationen finalizewäre dies nützlich?
nbro

3
@ MarkJeronimus - Eigentlich ist das irrelevant. Die finalize()Methode on für die Hauptklasse wird aufgerufen, wenn eine >> Instanz << der Klasse durch Speicherbereinigung erfasst wird, nicht, wenn die Hauptmethode beendet wird. Und außerdem die Hauptklasse kann Müll , bevor die Anwendung beendet gesammelt werden; zB in einer Multithread-App, in der der "Haupt" -Thread andere Threads erstellt und dann zurückkehrt. (In der Praxis wäre ein nicht standardmäßiger Klassenlader erforderlich ....)
Stephen C

379

Die finalizeMethode wird aufgerufen, wenn ein Objekt Müll sammeln soll. Dies kann jederzeit geschehen, nachdem es für die Speicherbereinigung in Frage kommt.

Beachten Sie, dass es durchaus möglich ist, dass ein Objekt niemals Müll sammelt (und daher finalizeniemals aufgerufen wird). Dies kann passieren, wenn das Objekt niemals für gc berechtigt ist (da es über die gesamte Lebensdauer der JVM erreichbar ist) oder wenn zwischen dem Zeitpunkt, zu dem das Objekt berechtigt wird, und dem Zeitpunkt, zu dem die JVM nicht mehr ausgeführt wird, tatsächlich keine Speicherbereinigung ausgeführt wird (dies geschieht häufig mit simple Testprogramme).

Es gibt Möglichkeiten, die JVM anzuweisen, finalizeauf Objekten ausgeführt zu werden, die noch nicht aufgerufen wurden, aber ihre Verwendung ist auch keine gute Idee (die Garantien dieser Methode sind auch nicht sehr stark).

Wenn Sie sich finalizefür den korrekten Betrieb Ihrer Anwendung verlassen, machen Sie etwas falsch. finalizesollte nur zur Bereinigung von (normalerweise nicht Java) Ressourcen verwendet werden. Und das genau deshalb , weil die JVM nicht garantiert, dass finalizejemals ein Objekt aufgerufen wird.


2
@ Rajesh. Nein, es ist kein Problem der "Lebensspanne". Sie können Ihr Programm (für Jahre) in eine Endlosschleife stellen, und wenn der Garbage Collector nicht benötigt wird, wird er niemals ausgeführt.
ChrisCantrell

5
@VikasVerma: Der perfekte Ersatz ist nichts: Sie sollten sie nicht brauchen. Der einzige Fall, in dem es sinnvoll wäre, wenn Ihre Klasse eine externe Ressource verwaltet (wie eine TCP / IP-Verbindung, eine Datei ... alles, was der Java GC nicht verarbeiten kann). In diesen Fällen ist die ClosableSchnittstelle (und die Idee dahinter) wahrscheinlich .close()genau das, was Sie möchten: Schließen / Verwerfen Sie die Ressource und fordern Sie den Benutzer Ihrer Klasse auf, sie zum richtigen Zeitpunkt aufzurufen. Sie könnten einen hinzufügen möchten finalizeMethode „nur speichern zu sein“, aber das wäre eher ein Debugging - Tool als eine tatsächliche Fix (weil es nicht zuverlässig genug ist).
Joachim Sauer

4
@Dragonborn: Das ist eigentlich eine ganz andere Frage und sollte separat gestellt werden. Es gibt Shutdown-Hooks, die jedoch nicht garantiert werden können, wenn die JVM unerwartet heruntergefahren wird (auch bekannt als Abstürze). Ihre Garantien sind jedoch deutlich höher als die für Finalisierer (und sie sind auch sicherer).
Joachim Sauer

4
In Ihrem letzten Absatz heißt es, dass Sie es nur zum Bereinigen von Ressourcen verwenden sollen, obwohl es keine Garantie gibt, dass es jemals aufgerufen wird. Spricht dieser Optimismus? Ich würde annehmen, dass etwas Unzuverlässiges, da dies auch nicht zum Aufräumen von Ressourcen geeignet wäre.
Jeroen Vannevel

2
Finalizer können manchmal den Tag retten ... Ich hatte einen Fall, in dem eine Bibliothek eines Drittanbieters einen FileInputStream verwendete und ihn nie schloss. Mein Code rief den Code der Bibliothek auf und versuchte dann, die Datei zu verschieben. Dies schlug fehl, weil sie noch geöffnet war. Ich musste den Aufruf erzwingen System.gc(), um FileInputStream :: finalize () aufzurufen, dann konnte ich die Datei verschieben.
Elist

73
protected void finalize() throws Throwable {}
  • Jede Klasse erbt die finalize()Methode von java.lang.Object
  • Die Methode wird vom Garbage Collector aufgerufen, wenn festgestellt wird, dass keine Verweise mehr auf das Objekt vorhanden sind
  • Die Object finalize-Methode führt keine Aktionen aus, kann jedoch von jeder Klasse überschrieben werden
  • Normalerweise sollte es überschrieben werden, um Nicht-Java-Ressourcen zu bereinigen, dh eine Datei zu schließen
  • Beim Überschreiben ist finalize()es eine gute Programmierpraxis, eine try-catch-finally-Anweisung zu verwenden und immer aufzurufen super.finalize(). Dies ist eine Sicherheitsmaßnahme, um sicherzustellen, dass Sie nicht versehentlich das Schließen einer Ressource verpassen, die von den aufrufenden Objekten der Klasse verwendet wird

    protected void finalize() throws Throwable {
         try {
             close();        // close open files
         } finally {
             super.finalize();
         }
     }
  • Jede Ausnahme, die finalize()während der Speicherbereinigung ausgelöst wird, stoppt die Finalisierung, wird jedoch ansonsten ignoriert

  • finalize() wird nie mehr als einmal auf einem Objekt ausgeführt

zitiert aus: http://www.janeg.ca/scjp/gc/finalize.html

Sie können diesen Artikel auch überprüfen:


25
Der JavaWorld-Artikel, auf den Sie verlinken, stammt aus dem Jahr 1998 und enthält einige interessante Ratschläge, insbesondere den Vorschlag, System.runFinalizersOnExit () aufzurufen, um sicherzustellen, dass Finalizer ausgeführt werden, bevor die JVM beendet wird. Diese Methode ist derzeit veraltet, mit dem Kommentar 'Diese Methode ist von Natur aus unsicher. Dies kann dazu führen, dass Finalizer für lebende Objekte aufgerufen werden, während andere Threads diese Objekte gleichzeitig bearbeiten, was zu unberechenbarem Verhalten oder Deadlock führt. ' Also werde ich das nicht tun.
Greg Chabala

3
Da runFinalizerOnExit () NICHT threadsicher ist, kann man Runtime.getRuntime () ausführen. AddShutdownHook (neuer Thread () {public void run () {destroyMyEnclosingClass ();}}); im Konstruktor der Klasse.
Ustaman Sangat

2
@Ustaman Sangat Das ist eine Möglichkeit, aber denken Sie daran, dass dies einen Verweis auf Ihre Instanz aus dem shutdownHook setzt, der so ziemlich garantiert, dass Ihre Klasse niemals Müll gesammelt wird. Mit anderen Worten, es ist ein Speicherverlust.
Pieroxy

1
@pieroxy, obwohl ich allen anderen hier zustimme, finalize () für nichts zu verwenden, verstehe ich nicht, warum es eine Referenz vom Shutdown-Hook geben müsste. Man könnte eine weiche Referenz haben.
Ustaman Sangat

25

Die Java- finalize()Methode ist kein Destruktor und sollte nicht zur Verarbeitung der Logik verwendet werden, von der Ihre Anwendung abhängt. Die Java-Spezifikation besagt, dass es keine Garantie dafür gibt, dass die finalizeMethode während der Lebensdauer der Anwendung überhaupt aufgerufen wird.

Was Sie wahrscheinlich wollen, ist eine Kombination aus finallyund eine Bereinigungsmethode, wie in:

MyClass myObj;

try {
    myObj = new MyClass();

    // ...
} finally {

    if (null != myObj) {
        myObj.cleanup();
    }
}

20

Schauen Sie sich Effective Java, 2. Ausgabe, Seite 27 an. Punkt 7: Vermeiden Sie Finalizer

Finalizer sind unvorhersehbar, oft gefährlich und im Allgemeinen unnötig. Mach niemals etwas zeitkritisches in einem Finalizer. Verlassen Sie sich niemals auf einen Finalizer, um den kritischen dauerhaften Status zu aktualisieren.

Verwenden Sie stattdessen try-finally, um eine Ressource zu beenden:

// try-finally block guarantees execution of termination method
Foo foo = new Foo(...);
try {
    // Do what must be done with foo
    ...
} finally {
    foo.terminate(); // Explicit termination method
}

3
oder versuchen Sie es mit Ressourcen
Gabriel Garcia

3
Dies setzt voraus, dass die Lebensdauer eines Objekts im Bereich einer Funktion liegt. Was natürlich nicht der Fall ist, auf den sich das OP bezieht, und es ist auch nicht der Fall, den grundsätzlich jeder braucht. Denken Sie an "Cache Engine Rückgabewert Referenzanzahl". Sie möchten den Cache-Eintrag freigeben, wenn die letzte Referenz freigegeben wird, wissen jedoch nicht, wann die letzte Referenz freigegeben wird. finalize () kann beispielsweise eine Ref-Anzahl verringern ... Wenn Ihre Benutzer jedoch explizit eine kostenlose Funktion aufrufen müssen, fragen Sie nach Speicherlecks. Normalerweise mache ich einfach beides (Release-Funktion + Double Check in Finalize ...) ...
Erik Aronesty

16

Wann wird die finalize()Methode in Java aufgerufen?

Die Finalize-Methode wird aufgerufen, nachdem der GC festgestellt hat, dass das Objekt nicht mehr erreichbar ist, und bevor der vom Objekt verwendete Speicher tatsächlich zurückgefordert wird.

  • Wenn ein Objekt niemals unerreichbar wird, finalize()wird es niemals aufgerufen.

  • Wenn der GC nicht ausgeführt wird, wird er finalize()möglicherweise nie aufgerufen. (Normalerweise wird der GC nur ausgeführt, wenn die JVM entscheidet, dass wahrscheinlich genug Müll vorhanden ist, damit es sich lohnt.)

  • Es kann mehr als einen GC-Zyklus dauern, bis der GC feststellt, dass ein bestimmtes Objekt nicht erreichbar ist. (Java-GCs sind normalerweise "Generationskollektoren" ...)

  • Sobald der GC feststellt, dass ein Objekt nicht erreichbar und finalisierbar ist, wird es in eine Finalisierungswarteschlange gestellt. Die Finalisierung erfolgt normalerweise asynchron mit dem normalen GC.

(Die JVM-Spezifikation erlaubt einer JVM tatsächlich , niemals Finalizer auszuführen ... vorausgesetzt, sie beansprucht nicht den von den Objekten verwendeten Speicherplatz zurück. Eine auf diese Weise implementierte JVM wäre verkrüppelt / nutzlos, aber dieses Verhalten ist "erlaubt". .)

Das Ergebnis ist, dass es unklug ist, sich auf die Finalisierung zu verlassen, um Dinge zu tun, die in einem bestimmten Zeitrahmen erledigt werden müssen. Es ist "Best Practice", sie überhaupt nicht zu verwenden. Es sollte einen besseren (dh zuverlässigeren) Weg geben, um das zu tun, was Sie mit der finalize()Methode versuchen .

Die einzige legitime Verwendung für die Finalisierung besteht darin, Ressourcen zu bereinigen, die mit Objekten verknüpft sind, die durch Anwendungscode verloren gegangen sind. Selbst dann sollten Sie versuchen, den Anwendungscode so zu schreiben, dass die Objekte überhaupt nicht verloren gehen. (Verwenden Sie beispielsweise Java 7+ Try-with-Resources, um sicherzustellen, dass dies close()immer aufgerufen wird ...)


Ich habe eine Testklasse erstellt, die in eine Datei schreibt, wenn die finalize () -Methode durch Überschreiben aufgerufen wird. Es wird nicht ausgeführt. Kann mir jemand den Grund sagen, warum es nicht ausgeführt wird?

Es ist schwer zu sagen, aber es gibt einige Möglichkeiten:

  • Das Objekt wird nicht durch Müll gesammelt, da es noch erreichbar ist.
  • Das Objekt wird nicht durch Müll gesammelt, da der GC nicht ausgeführt wird, bevor Ihr Test abgeschlossen ist.
  • Das Objekt wird vom GC gefunden und vom GC in die Finalisierungswarteschlange gestellt. Die Finalisierung wird jedoch nicht abgeschlossen, bevor Ihr Test abgeschlossen ist.

10

Da es eine Unsicherheit beim Aufrufen der Methode finalize () durch JVM gibt (nicht sicher, ob finalized (), die überschrieben wird, ausgeführt wird oder nicht), ist es für Studienzwecke besser zu beobachten, was passiert, wenn finalize () aufgerufen wird Erzwingen Sie, dass die JVM die Garbage Collection per Befehl aufruft System.gc().

Insbesondere wird finalize () aufgerufen, wenn ein Objekt nicht mehr verwendet wird. Aber wenn wir versuchen, es durch Erstellen neuer Objekte aufzurufen, gibt es keine Gewissheit über seinen Aufruf. Aus Sicherheitsgründen erstellen wir ein nullObjekt, cdas offensichtlich keine zukünftige Verwendung hat. Daher sehen wir den cFinalize-Aufruf des Objekts .

Beispiel

class Car {

    int maxspeed;

    Car() {
        maxspeed = 70;
    }

    protected void finalize() {

    // Originally finalize method does nothing, but here we override finalize() saying it to print some stmt
    // Calling of finalize is uncertain. Difficult to observe so we force JVM to call it by System.gc(); GarbageCollection

        System.out.println("Called finalize method in class Car...");
    }
}

class Bike {

    int maxspeed;

    Bike() {
        maxspeed = 50;
    }

    protected void finalize() {
        System.out.println("Called finalize method in class Bike...");
    }
}

class Example {

    public static void main(String args[]) {
        Car c = new Car();
        c = null;    // if c weren`t null JVM wouldn't be certain it's cleared or not, null means has no future use or no longer in use hence clears it
        Bike b = new Bike();
        System.gc();    // should clear c, but not b
        for (b.maxspeed = 1; b.maxspeed <= 70; b.maxspeed++) {
            System.out.print("\t" + b.maxspeed);
            if (b.maxspeed > 50) {
                System.out.println("Over Speed. Pls slow down.");
            }
        }
    }
}

Ausgabe

    Called finalize method in class Car...
            1       2       3       4       5       6       7       8       9
    10      11      12      13      14      15      16      17      18      19
    20      21      22      23      24      25      26      27      28      29
    30      31      32      33      34      35      36      37      38      39
    40      41      42      43      44      45      46      47      48      49
    50      51Over Speed. Pls slow down.
            52Over Speed. Pls slow down.
            53Over Speed. Pls slow down.
            54Over Speed. Pls slow down.
            55Over Speed. Pls slow down.
            56Over Speed. Pls slow down.
            57Over Speed. Pls slow down.
            58Over Speed. Pls slow down. 
            59Over Speed. Pls slow down.
            60Over Speed. Pls slow down.
            61Over Speed. Pls slow down.
            62Over Speed. Pls slow down.
            63Over Speed. Pls slow down.
            64Over Speed. Pls slow down.
            65Over Speed. Pls slow down.
            66Over Speed. Pls slow down.
            67Over Speed. Pls slow down.
            68Over Speed. Pls slow down.
            69Over Speed. Pls slow down.
            70Over Speed. Pls slow down.

Hinweis - Auch nach dem Drucken von bis zu 70 und danach, wenn das Objekt b nicht im Programm verwendet wird, besteht die Unsicherheit, dass b von JVM gelöscht wird oder nicht, da "Aufgerufene Finalisierungsmethode in Klasse Bike ..." nicht gedruckt wird.


10
Das Aufrufen System.gc();ist keine Garantie dafür, dass die Garbage Collection tatsächlich ausgeführt wird.
Simon Forsberg

3
Es ist auch nicht garantiert, welche Art von Sammlung ausgeführt wird. Relevant, da die meisten Java-GCs "Generationskollektoren" sind.
Stephen C

5

finalize druckt die Anzahl für die Klassenerstellung aus.

protected void finalize() throws Throwable {
    System.out.println("Run F" );
    if ( checkedOut)
        System.out.println("Error: Checked out");
        System.out.println("Class Create Count: " + classCreate);
}

Main

while ( true) {
    Book novel=new Book(true);
    //System.out.println(novel.checkedOut);
    //Runtime.getRuntime().runFinalization();
    novel.checkIn();
    new Book(true);
    //System.runFinalization();
    System.gc();

Wie du siehst. Die folgende Ausgabe zeigt, dass der GC zum ersten Mal ausgeführt wurde, wenn die Klassenanzahl 36 beträgt.

C:\javaCode\firstClass>java TerminationCondition
Run F
Error: Checked out
Class Create Count: 36
Run F
Error: Checked out
Class Create Count: 48
Run F

4

Nachdem ich in letzter Zeit mit Finalizer-Methoden gerungen habe (um Verbindungspools während des Testens zu entsorgen), muss ich sagen, dass dem Finalizer viele Dinge fehlen. Unter Verwendung von VisualVM zum Beobachten sowie unter Verwendung schwacher Referenzen zum Verfolgen der tatsächlichen Interaktion stellte ich fest, dass die folgenden Dinge in einer Java 8-Umgebung (Oracle JDK, Ubuntu 15) zutreffen:

  • Finalize wird nicht sofort aufgerufen. Der Finalizer (GC-Teil) besitzt die Referenz einzeln
  • Der Standard-Garbage Collector bündelt nicht erreichbare Objekte
  • Finalize wird in großen Mengen aufgerufen und zeigt auf ein Implementierungsdetail, dass es eine bestimmte Phase gibt, in der der Garbage Collector die Ressourcen freigibt.
  • Das Aufrufen von System.gc () führt häufig nicht dazu, dass Objekte häufiger finalisiert werden. Es führt lediglich dazu, dass der Finalizer schneller auf ein nicht erreichbares Objekt aufmerksam wird
  • Das Erstellen eines Thread-Dumps führt fast immer dazu, dass der Finalizer aufgrund des hohen Heap-Overheads während der Ausführung des Heap-Dumps oder eines anderen internen Mechanismus ausgelöst wird
  • Finalisierungsnähte müssen entweder an den Speicherbedarf (mehr Speicherplatz freigeben) oder an die Liste der Objekte gebunden werden, die für das Finalisieren eines bestimmten internen Grenzwerts markiert werden. Wenn also viele Objekte finalisiert werden, wird die Finalisierungsphase im Vergleich zu nur wenigen Objekten häufiger und früher ausgelöst
  • Es gab Umstände, unter denen ein System.gc () eine Finalisierung direkt auslöste, jedoch nur, wenn die Referenz lokal und kurzlebig war. Dies kann generationsbezogen sein.

Letzter Gedanke

Die Finalisierungsmethode ist unzuverlässig, kann jedoch nur für eine Sache verwendet werden. Sie können sicherstellen, dass ein Objekt vor der Müllabfuhr geschlossen oder entsorgt wurde, um eine Ausfallsicherung zu implementieren, wenn Objekte mit einem komplexeren Lebenszyklus, der eine Aktion am Lebensende beinhaltet, korrekt behandelt werden. Das ist der einzige Grund, warum ich mir vorstellen kann, dass es sich lohnt, es zu überschreiben.


2

Ein Objekt kann für die Garbage Collection oder GC verwendet werden, wenn es nicht über Live-Threads oder statische Aktualisierungen erreichbar ist. Mit anderen Worten, Sie können sagen, dass ein Objekt für die Garbage Collection berechtigt ist, wenn alle Verweise null sind. Zyklische Abhängigkeiten werden nicht als Referenz gezählt. Wenn also Objekt A eine Referenz von Objekt B und Objekt B eine Referenz von Objekt A hat und keine andere Live-Referenz hat, können sowohl Objekt A als auch B für die Garbage Collection verwendet werden. Im Allgemeinen kann ein Objekt in folgenden Fällen in Java zur Speicherbereinigung verwendet werden:

  1. Alle Referenzen dieses Objekts werden explizit auf null gesetzt, z. B. object = null
  2. Das Objekt wird innerhalb eines Blocks erstellt und die Referenz verlässt den Gültigkeitsbereich, sobald die Steuerung diesen Block verlässt.
  3. Das übergeordnete Objekt wird auf null gesetzt, wenn ein Objekt die Referenz eines anderen Objekts enthält und wenn Sie die Referenz des Containerobjekts auf null setzen, wird das untergeordnete oder enthaltene Objekt automatisch für die Speicherbereinigung zugelassen.
  4. Wenn ein Objekt nur Live-Referenzen über WeakHashMap hat, kann es zur Speicherbereinigung verwendet werden.

Wenn ein finalFeld eines Objekts während einer langsamen Berechnung wiederholt verwendet wird und das Objekt danach nie mehr verwendet wird, wird das Objekt bis zum letzten Mal am Leben gehalten, wenn der Quellcode das Feld anfordert, oder könnte die JIT das Feld in ein Objekt kopieren temporäre Variable und dann das Objekt vor der Berechnung verlassen?
Supercat

@supercat: Ein Optimierer kann Code in ein Formular umschreiben, in dem das Objekt überhaupt nicht erstellt wird. In diesem Fall wird es möglicherweise direkt nach Abschluss des Konstruktors finalisiert, es sei denn, die Synchronisierung erzwingt eine Reihenfolge zwischen der Verwendung des Objekts und dem Finalizer.
Holger

@Holger: In Fällen, in denen die JIT alles sehen kann, was jemals mit einem Objekt zwischen seiner Erstellung und Aufgabe geschehen wird, sehe ich keinen Grund für die JIT, den Finalizer vorzeitig abzufeuern. Die eigentliche Frage wäre, was Code tun muss, um sicherzustellen, dass der Finalizer innerhalb einer bestimmten Methode nicht ausgelöst werden kann. In .NET gibt es eine GC.KeepAlive()Funktion, die nichts anderes tut, als den GC zu zwingen, anzunehmen, dass er möglicherweise ein Objekt verwendet, aber ich kenne keine solche Funktion in Java. Man könnte eine volatileVariable für diesen Zweck verwenden, aber die Verwendung einer Variablen nur für einen solchen Zweck erscheint verschwenderisch.
Supercat

@supercat: Die JIT löst keine Finalisierung aus, sondern ordnet den Code nur so an, dass keine Referenz gespeichert wird. Es kann jedoch von Vorteil sein, die direkt in die Warteschlange zu stellen FinalizerReference, sodass kein GC-Zyklus erforderlich ist, um herauszufinden, dass keine vorhanden sind Verweise. Die Synchronisation ist ausreichend , um ein , um sicherzustellen , geschieht-vor - Beziehung; Da die Finalisierung möglicherweise (tatsächlich) in einem anderen Thread ausgeführt wird, ist sie häufig ohnehin formal erforderlich. Java 9 wird hinzufügen Reference.reachabilityFence...
Holger

@Holger: Wenn die JIT die Objekterstellung optimiert, wird nur Finalizeaufgerufen, wenn die JIT Code erzeugt, der dies direkt tut . Würde Synchronisationscode normalerweise für Objekte erwartet, die nur in einem einzelnen Thread verwendet werden sollen? Wenn ein Objekt eine Aktion ausführt, die rückgängig gemacht werden muss, bevor es abgebrochen wird (z. B. das Öffnen einer Socket-Verbindung und die ausschließliche Verwendung der Ressource am anderen Ende), wäre es eine Katastrophe, wenn ein Finalizer die Verbindung schließt, während der Code noch den Socket verwendet . Wäre es normal, dass Code die Synchronisation verwendet ...
Supercat

2

Die Finalisierungsmethode kann nicht garantiert werden. Diese Methode wird aufgerufen, wenn das Objekt für die GC in Frage kommt. Es gibt viele Situationen, in denen die Objekte möglicherweise nicht durch Müll gesammelt werden.


3
Falsch. Was Sie sagen, ist, dass ein Objekt finalisiert wird, wenn es nicht mehr erreichbar ist. Es wird tatsächlich aufgerufen, wenn die Methode tatsächlich erfasst wird.
Stephen C

2

Manchmal, wenn es zerstört wird, muss ein Objekt eine Aktion ausführen. Wenn ein Objekt beispielsweise über eine Nicht-Java-Ressource wie ein Dateihandle oder eine Schriftart verfügt, können Sie überprüfen, ob diese Ressourcen freigegeben sind, bevor Sie ein Objekt zerstören. Um solche Situationen zu verwalten, bietet Java einen Mechanismus namens "Finalisieren". Durch Abschluss können Sie bestimmte Aktionen definieren, die auftreten, wenn ein Objekt aus dem Garbage Collector entfernt werden soll. Um einer Klasse einen Finalizer hinzuzufügen, definieren Sie einfach die Methode finalize () . Die Java-Ausführungszeit ruft diese Methode immer dann auf, wenn ein Objekt dieser Klasse gelöscht werden soll. Innerhalb der Finalisierungsmethode ()Sie geben Aktionen an, die ausgeführt werden sollen, bevor ein Objekt zerstört wird. Der Garbage Collector wird regelmäßig nach Objekten durchsucht, die sich nicht mehr auf einen laufenden Status oder indirekt auf ein anderes Objekt mit Referenz beziehen. Bevor ein Asset freigegeben wird, ruft die Java-Laufzeit die Methode finalize () -Methode für das Objekt auf. Diefinalize () auf und hat die folgende allgemeine Form:

protected void finalize(){
    // This is where the finalization code is entered
}

Mit dem geschützten Schlüsselwort wird der Zugriff auf finalize () durch Code außerhalb seiner Klasse verhindert. Es ist wichtig zu verstehen, dass finalize () unmittelbar vor der Garbage Collection aufgerufen wird. Es wird beispielsweise nicht aufgerufen, wenn ein Objekt den Bereich verlässt. Dies bedeutet, dass Sie nicht wissen können, wann oder ob finalize () ausgeführt wird. Infolgedessen muss das Programm andere Mittel bereitstellen, um Systemressourcen oder andere vom Objekt verwendete Ressourcen freizugeben. Sie sollten sich nicht auf finalize () verlassen, um das Programm normal auszuführen.


1

Klasse, in der wir die Finalisierungsmethode überschreiben

public class TestClass {    
    public TestClass() {
        System.out.println("constructor");
    }

    public void display() {
        System.out.println("display");
    }
    @Override
    public void finalize() {
        System.out.println("destructor");
    }
}

Die Chancen, dass die Finalisierungsmethode aufgerufen wird

public class TestGarbageCollection {
    public static void main(String[] args) {
        while (true) {
            TestClass s = new TestClass();
            s.display();
            System.gc();
        }
    }
}

Wenn der Speicher mit Dump-Objekten überladen ist, ruft gc die finalize-Methode auf

Führen Sie die Konsole aus, in der die Finalize-Methode nicht häufig aufgerufen wird. Wenn der Speicher überlastet wird, wird die Finalize-Methode aufgerufen.


0

Mit Java können Objekte eine Methode namens finalize () implementieren, die möglicherweise aufgerufen wird.

Die Methode finalize () wird aufgerufen, wenn der Garbage Collector versucht, das Objekt zu sammeln.

Wenn der Garbage Collector nicht ausgeführt wird, wird die Methode nicht aufgerufen.

Wenn der Garbage Collector das Objekt nicht erfasst und erneut versucht, es auszuführen , wird die Methode beim zweiten Mal nicht aufgerufen.

In der Praxis ist es sehr unwahrscheinlich, dass Sie es in realen Projekten verwenden.

Denken Sie daran, dass es möglicherweise nicht aufgerufen wird und dass es definitiv nicht zweimal aufgerufen wird. Die Methode finalize () kann null oder einmal ausgeführt werden.

Im folgenden Code erzeugt die Methode finalize () keine Ausgabe, wenn sie ausgeführt wird, da das Programm beendet wird, bevor der Garbage Collector ausgeführt werden muss.

Quelle


0

finalize()wird kurz vor der Speicherbereinigung aufgerufen. Es wird nicht aufgerufen, wenn ein Objekt den Gültigkeitsbereich verlässt. Dies bedeutet, dass Sie nicht wissen können, wann oder ob finalize()sie ausgeführt werden.

Beispiel:

Wenn Ihr Programm endet, bevor der Garbage Collector auftritt, finalize()wird es nicht ausgeführt. Daher sollte es als Sicherungsverfahren verwendet werden, um den ordnungsgemäßen Umgang mit anderen Ressourcen sicherzustellen, oder für Anwendungen mit besonderer Verwendung, nicht als Mittel, das Ihr Programm im normalen Betrieb verwendet.


0

Wie in https://wiki.sei.cmu.edu/confluence/display/java/MET12-J.+Do+not+use+finalizers ausgeführt ,

Es gibt keinen festen Zeitpunkt, zu dem Finalizer ausgeführt werden müssen, da der Ausführungszeitpunkt von der Java Virtual Machine (JVM) abhängt. Die einzige Garantie besteht darin, dass jede Finalizer-Methode, die ausgeführt wird, dies irgendwann tut, nachdem das zugeordnete Objekt nicht mehr erreichbar ist (erkannt während des ersten Zyklus der Speicherbereinigung) und irgendwann bevor der Speicherbereiniger den Speicher des zugeordneten Objekts zurückerobert (während des zweiten Zyklus des Speicherbereinigers). . Die Ausführung des Finalisierers eines Objekts kann beliebig lange verzögert werden, nachdem das Objekt nicht mehr erreichbar ist. Folglich ist das Aufrufen zeitkritischer Funktionen wie das Schließen von Dateihandles in der finalize () -Methode eines Objekts problematisch.


-3

Versuchen Sie, dieses Programm zum besseren Verständnis auszuführen

public class FinalizeTest 
{       
    static {
        System.out.println(Runtime.getRuntime().freeMemory());
    }

    public void run() {
        System.out.println("run");
        System.out.println(Runtime.getRuntime().freeMemory());
    }

     protected void finalize() throws Throwable { 
         System.out.println("finalize");
         while(true)
             break;          
     }

     public static void main(String[] args) {
            for (int i = 0 ; i < 500000 ; i++ ) {
                    new FinalizeTest().run();
            }
     }
}
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.