Ich muss wissen, wann die [finalize()
](https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#finalize()) Methode wird in der JVM
. Ich habe eine Testklasse erstellt, die in eine Datei schreibt, wenn die finalize()
Methode aufgerufen wird, indem sie überschrieben wird. Sie wird nicht ausgeführt. Kann mir jemand den Grund dafür nennen, warum sie nicht ausgeführt wird?
Antworten
Zu viele Anzeigen?Wann ist 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 er den vom Objekt verwendeten Speicher zurückfordert.
-
Wenn ein Objekt niemals unerreichbar wird,
finalize()
wird nie darauf angesprochen werden. -
Wenn der GC nicht läuft, dann
finalize()
darf niemals angerufen werden. (Normalerweise läuft die GC nur, wenn die JVM entscheidet, dass es wahrscheinlich genug Müll gibt, um es lohnenswert zu machen.) -
Es kann mehr als einen GC-Zyklus dauern, bis der GC feststellt, dass ein bestimmtes Objekt unerreichbar ist. (Java GCs sind typischerweise "generative" Kollektoren ...)
-
Sobald der GC feststellt, dass ein Objekt unerreichbar und finalisierbar ist, wird es in eine Finalisierungswarteschlange gestellt. Die Finalisierung erfolgt in der Regel asynchron mit der normalen GC.
(Die JVM-Spezifikation sieht vor ermöglicht eine JVM zu niemals Finalizer ausführen ... vorausgesetzt, es wird nicht der von den Objekten belegte Platz zurückverlangt. Eine JVM, die auf diese Weise implementiert wurde, wäre verkrüppelt / nutzlos, aber es ist dieses Verhalten "erlaubt").
Das Fazit ist, dass es unklug ist, sich auf den Abschluss zu verlassen, um Dinge zu tun, die innerhalb eines bestimmten Zeitrahmens erledigt werden müssen. Es ist die "beste Praxis", sie überhaupt nicht zu verwenden. Es sollte einen besseren (d. h. zuverlässigeren) Weg geben, um das zu tun, was Sie in der finalize()
Methode.
Die einzige legitime Verwendung für die Finalisierung ist die Bereinigung von Ressourcen, die mit Objekten verbunden sind, die durch Anwendungscode verloren gegangen sind. Selbst dann sollten Sie versuchen, den Anwendungscode so zu schreiben, dass er die Objekte gar nicht erst verliert. (Verwenden Sie zum Beispiel Java 7+ Versuch-mit-Ressourcen um sicherzustellen, dass close()
wird immer aufgerufen ...)
Ich habe eine Testklasse erstellt, die in eine Datei schreibt, wenn die finalize()-Methode aufgerufen wird, indem sie überschrieben wird. Sie wird nicht ausgeführt. Kann mir jemand den Grund dafür nennen, warum sie nicht ausgeführt wird?
Es ist schwer zu sagen, aber es gibt einige Möglichkeiten:
- Das Objekt wird nicht garbage collected, da es noch erreichbar ist.
- Das Objekt wird nicht garbage collected, weil die GC nicht ausgeführt wird, bevor Ihr Test beendet ist.
- Das Objekt wird von der GC gefunden und von der GC in die Finalisierungswarteschlange gestellt, aber die Finalisierung wird nicht abgeschlossen, bevor Ihr Test beendet ist.
Da der Aufruf der finalize()-Methode durch die JVM mit Unsicherheit behaftet ist (es ist nicht sicher, ob finalize(), das überschrieben wird, ausgeführt wird oder nicht), ist es für Studienzwecke besser, die JVM zu zwingen, die Garbage Collection mit dem Befehl System.gc()
.
Insbesondere wird finalize() aufgerufen, wenn ein Objekt nicht mehr in Gebrauch ist. Aber wenn wir versuchen, es aufzurufen, indem wir neue Objekte erstellen, gibt es keine Gewissheit über seinen Aufruf. Um sicher zu gehen, erstellen wir also eine null
Objekt c
die offensichtlich keine zukünftige Verwendung hat, daher sehen wir das Objekt c
Aufruf von finalize.
Exemple
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.
Note - Auch nach dem Drucken bis 70 und nach dem Objekt b nicht im Programm verwendet wird, gibt es Unsicherheit, ob b von der JVM gelöscht wird oder nicht, da "Called finalize method in class Bike..." nicht gedruckt wird.
Finalize gibt den Zählerstand 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);
}
principal
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 Sie sehen können. Das folgende Beispiel zeigt, dass die gc zum ersten Mal ausgeführt wird, wenn die Anzahl der Klassen 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
Nachdem ich mich in letzter Zeit mit Finalizer-Methoden herumgeschlagen habe (um Verbindungspools während des Testens zu entsorgen), muss ich sagen, dass Finalizer viele Dinge vermissen lässt. Mit VisualVM zu beobachten sowie schwache Referenzen verwenden, um die tatsächliche Interaktion zu verfolgen, fand ich, dass folgende Dinge in einer Java 8 Umgebung (Oracle JDK, Ubuntu 15) wahr sind:
- Finalize wird nicht sofort aufgerufen, da der Finalizer (GC-Teil) den Verweis individuell und ausweichend besitzt
- Der standardmäßige Garbage Collector sammelt unerreichbare Objekte
- Finalize wird in der Masse aufgerufen und weist auf ein Implementierungsdetail hin, dass es eine bestimmte Phase gibt, in der der Garbage Collector die Ressourcen freigibt.
- Ein häufiger Aufruf von System.gc() führt nicht dazu, dass Objekte häufiger finalisiert werden, sondern lediglich dazu, dass der Finalizer schneller auf ein unerreichbares Objekt aufmerksam wird.
- Das Erstellen eines Thread-Dumps führt fast immer dazu, dass der Finalizer aufgrund des hohen Heap-Overheads während der Durchführung des Heap-Dumps oder eines anderen internen Mechanismus ausgelöst wird
- Die Finalisierung scheint entweder durch den Speicherbedarf (mehr Speicher freigeben) oder durch die Liste der für die Finalisierung markierten Objekte, die eine bestimmte interne Grenze überschreitet, begrenzt zu sein. Wenn also viele Objekte finalisiert werden, wird die Finalisierungsphase häufiger und früher ausgelöst, als wenn es nur wenige sind.
- Es gab Fälle, in denen ein System.gc() direkt ein finalize auslöste, aber nur, wenn die Referenz eine lokale und kurzlebige war. Dies könnte generationsbedingt sein.
Letzter Gedanke
Die Finalize-Methode ist unzuverlässig, kann aber nur für eine Sache verwendet werden. Sie können sicherstellen, dass ein Objekt geschlossen oder entsorgt wurde, bevor es in den Müll geworfen wurde. Damit ist es möglich, einen Fail Safe zu implementieren, wenn Objekte mit einem komplexeren Lebenszyklus, der eine End-of-Life-Aktion beinhaltet, korrekt behandelt werden. Das ist der einzige Grund, der mir einfällt, warum es sich lohnt, diese Funktion außer Kraft zu setzen.
Ein Objekt kommt für die Garbage Collection oder GC in Frage, wenn es von keinem Live-Thread oder einer statischen Referenz erreichbar ist. Mit anderen Worten kann man sagen, dass ein Objekt für die Garbage Collection in Frage kommt, wenn alle Referenzen null sind. Zyklische Abhängigkeiten werden nicht als Referenz gezählt, d.h. wenn Objekt A eine Referenz von Objekt B hat und Objekt B eine Referenz von Objekt A hat und sie keine andere Live-Referenz haben, dann kommen beide Objekte A und B für die Garbage Collection in Frage. Im Allgemeinen kommt ein Objekt für die Garbage Collection in Java in den folgenden Fällen in Frage:
- Alle Verweise auf dieses Objekt werden ausdrücklich auf null gesetzt, z. B. object = null
- Das Objekt wird innerhalb eines Blocks erstellt und der Verweis verlässt den Bereich, sobald die Kontrolle den Block verlässt.
- Übergeordnetes Objekt auf Null gesetzt, wenn ein Objekt einen Verweis auf ein anderes Objekt enthält, und wenn Sie den Verweis des Containerobjekts auf Null setzen, wird das untergeordnete oder enthaltene Objekt automatisch für die Garbage Collection zugelassen.
- Wenn ein Objekt nur Live-Referenzen über WeakHashMap hat, kommt es für die Garbage Collection in Frage.