it-swarm.com.de

Verbessert die Verwendung von final für Variablen in Java Garbage Collection?

Heute diskutieren meine Kollegen und ich über die Verwendung des Schlüsselworts final in Java), um die Garbage Collection zu verbessern.

Zum Beispiel, wenn Sie eine Methode schreiben wie:

public Double doCalc(final Double value)
{
   final Double maxWeight = 1000.0;
   final Double totalWeight = maxWeight * value;
   return totalWeight;  
}

Das Deklarieren der Variablen in der Methode final würde der Garbage Collection helfen, den Speicher von den nicht verwendeten Variablen in der Methode zu bereinigen, nachdem die Methode beendet wurde.

Ist das wahr?

82
Goran Martinic

Hier ist ein etwas anderes Beispiel, eines mit endgültigen Referenzfeldern anstelle der endgültigen lokalen Variablen vom Werttyp:

public class MyClass {

   public final MyOtherObject obj;

}

Jedes Mal, wenn Sie eine Instanz von MyClass erstellen, wird ein ausgehender Verweis auf eine MyOtherObject-Instanz erstellt, und der GC muss diesem Link folgen, um nach aktiven Objekten zu suchen.

Die JVM verwendet einen Mark-Sweep-GC-Algorithmus, der alle Live-Referenzen an den GC-Stammpositionen (wie alle Objekte im aktuellen Aufrufstapel) untersuchen muss. Jedes lebende Objekt wird als lebendig "markiert", und jedes Objekt, auf das von einem lebenden Objekt verwiesen wird, wird ebenfalls als lebendig markiert.

Nach Abschluss der Markierungsphase tastet der GC den Heap ab und gibt Speicher für alle nicht markierten Objekte frei (und komprimiert den Speicher für die verbleibenden aktiven Objekte).

Es ist auch wichtig zu erkennen, dass der Java Heap-Speicher in eine "junge Generation" und eine "alte Generation" aufgeteilt ist. Alle Objekte werden anfänglich in der jungen Generation (manchmal als "" bezeichnet) zugeordnet. Da die meisten Objekte kurzlebig sind, befasst sich der GC aggressiver mit der Befreiung des jüngsten Mülls von der jungen Generation. Wenn ein Objekt einen Sammelzyklus der jungen Generation überlebt, wird es in die alte Generation verschoben (auf die manchmal Bezug genommen wird) als "Tenured Generation"), die weniger häufig verarbeitet wird.

Also sage ich auf den ersten Blick: "Nein, der 'letzte' Modifikator hilft dem GC nicht, seine Arbeitsbelastung zu reduzieren.".

Meiner Meinung nach besteht die beste Strategie zur Optimierung Ihres Speichermanagements in Java) darin, falsche Referenzen so schnell wie möglich zu entfernen. Sie können dies tun, indem Sie einer Objektreferenz so bald wie möglich "null" zuweisen Du bist fertig damit.

Oder, noch besser, minimieren Sie die Größe jedes Deklarationsbereichs. Wenn Sie beispielsweise ein Objekt am Anfang einer 1000-Zeilen-Methode deklarieren und das Objekt bis zum Ende des Gültigkeitsbereichs dieser Methode (der letzten geschweiften Klammer) am Leben bleibt, bleibt das Objekt möglicherweise viel länger am Leben als tatsächlich notwendig.

Wenn Sie kleine Methoden mit nur etwa einem Dutzend Codezeilen verwenden, werden die Objekte, die in dieser Methode deklariert sind, schneller aus dem Geltungsbereich herausfallen, und der GC kann den größten Teil seiner Arbeit mit der viel effizienteren Methode ausführen junge Generation. Sie möchten nicht, dass Objekte in die ältere Generation verschoben werden, es sei denn, dies ist unbedingt erforderlich.

81
benjismith

Das Deklarieren einer lokalen Variablen final hat keine Auswirkungen auf die Speicherbereinigung. Sie können die Variable nur nicht ändern. Ihr Beispiel oben sollte nicht kompiliert werden, da Sie die Variable totalWeight ändern, die als final markiert wurde. Wenn Sie dagegen ein Primitiv (double anstelle von Double) final deklarieren, kann diese Variable in den aufrufenden Code eingefügt werden, wodurch Speicherplatz und Leistung beeinträchtigt werden können Verbesserung. Dies wird verwendet, wenn Sie eine Anzahl von public static final Strings in einer Klasse.

Im Allgemeinen optimieren der Compiler und die Laufzeit, wo es möglich ist. Es ist am besten, den Code entsprechend zu schreiben und nicht zu trickreich zu sein. Verwenden Sie final, wenn die Variable nicht geändert werden soll. Angenommen, der Compiler führt einfache Optimierungen durch. Wenn Sie sich Sorgen über die Leistung oder die Speichernutzung machen, ermitteln Sie das eigentliche Problem mithilfe eines Profilers.

37
Aaron

Nein, das stimmt nachdrücklich nicht.

Denken Sie daran, dass final keine Konstante bedeutet, sondern nur, dass Sie die Referenz nicht ändern können.

final MyObject o = new MyObject();
o.setValue("foo"); // Works just fine
o = new MyObject(); // Doesn't work.

Möglicherweise gibt es eine kleine Optimierung, die auf dem Wissen basiert, dass die JVM die Referenz niemals ändern muss (z. B. nicht überprüft werden muss, ob sie sich geändert hat), aber sie ist so gering, dass sie keine Sorgen macht.

Final sollte als nützliche Metadaten für den Entwickler und nicht als Compileroptimierung angesehen werden.

25
SCdF

Einige Punkte zu klären:

  • Das Nullen der Referenz sollte der GC nicht helfen. Wenn dies der Fall wäre, würde dies darauf hinweisen, dass Ihre Variablen einen zu großen Geltungsbereich haben. Eine Ausnahme ist der Fall des Objektnepotismus.

  • In Java gibt es noch keine On-Stack-Zuordnung.

  • Wenn Sie eine Variable final deklarieren, können Sie dieser Variablen (unter normalen Bedingungen) keinen neuen Wert zuweisen. Da final nichts über den Umfang aussagt, sagt es nichts über den Effekt auf die GC aus.

15
Kirk

Nun, ich weiß nichts über die Verwendung des Modifikators "final" in diesem Fall oder dessen Auswirkung auf den GC.

Aber ich kann sage Ihnen Folgendes: Wenn Sie Boxed-Werte anstelle von Primitiven verwenden (z. B. Double anstelle von Double), werden diese Objekte eher auf dem Heap als auf dem Stack zugeordnet und es entsteht unnötiger Müll, den der GC erzeugt muss aufräumen.

Ich verwende Boxed Primitives nur, wenn dies für eine vorhandene API erforderlich ist oder wenn ich nullfähige Primatives benötige.

11
benjismith

Endvariablen können nach der anfänglichen Zuweisung nicht mehr geändert werden (vom Compiler erzwungen).

Dies ändert nichts am Verhalten von Garbage Collection als solchem. Die einzige Sache ist, dass diese Variablen nicht auf Null gesetzt werden können, wenn sie nicht mehr verwendet werden (was die Garbage Collection in Situationen mit wenig Speicher unterstützen kann).

Sie sollten wissen, dass final es dem Compiler ermöglicht, Annahmen darüber zu treffen, was zu optimieren ist. Inlining-Code und ohne Code, von dem bekannt ist, dass er nicht erreichbar ist.

final boolean debug = false;

......

if (debug) {
  System.out.println("DEBUG INFO!");
}

Der Ausdruck wird nicht in den Byte-Code aufgenommen.

GC wirkt auf nicht erreichbare Refs. Dies hat nichts mit "endgültig" zu tun, was lediglich eine Behauptung einer einmaligen Zuordnung ist. Ist es möglich, dass einige VMs GC "final" verwenden können? Ich verstehe nicht wie oder warum.

3
dongilmore

final für lokale Variablen und Parameter hat keinen Einfluss auf die erzeugten Klassendateien und kann daher die Laufzeitleistung nicht beeinflussen. Wenn eine Klasse keine Unterklassen hat, behandelt HotSpot diese Klasse sowieso als endgültig (sie kann später rückgängig gemacht werden, wenn eine Klasse geladen wird, die diese Annahme verletzt). Ich glaube, dass final on Methoden den Klassen sehr ähnlich sind. final im statischen Feld kann es ermöglichen, die Variable als "Kompilierzeitkonstante" zu interpretieren und die Optimierung auf dieser Basis durch javac durchzuführen. final in Feldern lässt der JVM die Freiheit, happen-before -Relationen zu ignorieren.

Es gibt einen nicht so bekannten Eckfall mit Müllsammlern der Generation. (Für eine kurze Beschreibung lesen Sie die Antwort von benjismith für einen tieferen Einblick lesen Sie die Artikel am Ende).

Die Idee bei Generationen-GCs ist, dass meist nur junge Generationen berücksichtigt werden müssen. Der Stammspeicherort wird nach Referenzen durchsucht, und anschließend werden die Objekte der jungen Generation durchsucht. Während dieser häufigeren Sweeps werden keine Objekte der alten Generation überprüft.

Das Problem liegt nun darin, dass ein Objekt keine Referenzen auf jüngere Objekte haben darf. Wenn ein langlebiges Objekt (alte Generation) einen Verweis auf ein neues Objekt erhält, muss dieser Verweis vom Garbage Collector explizit nachverfolgt werden (siehe Artikel von IBM zum Hotspot-JVM-Collector ), der sich tatsächlich auf den GC auswirkt Performance.

Der Grund, warum ein altes Objekt nicht auf ein jüngeres verweisen kann, ist, dass, da das alte Objekt in untergeordneten Sammlungen nicht überprüft wird, der einzige Verweis auf das Objekt im alten Objekt nicht markiert wird und falsch wäre während der Sweep-Phase freigegeben.

Natürlich wirkt sich das letzte Schlüsselwort, wie von vielen erwähnt, nicht wirklich auf den Garbage Collector aus, aber es garantiert, dass die Referenz niemals in ein jüngeres Objekt geändert wird, wenn dieses Objekt die kleineren Sammlungen überlebt und es zum älteren Heap macht.

Artikel:

IBM über Garbage Collection: Verlauf , in der Hotspot-JVM und Leistung . Diese sind möglicherweise nicht mehr vollständig gültig, da sie aus dem Jahr 2003/04 stammen, bieten jedoch leicht lesbare Einblicke in GCs.

So am Tuning Garbage Collection

Es scheint eine Menge Antworten zu geben, die Vermutungen widerspiegeln. Die Wahrheit ist, es gibt keinen finalen Modifikator für lokale Variablen auf Bytecode-Ebene. Die virtuelle Maschine wird nie erfahren, dass Ihre lokalen Variablen als final definiert wurden oder nicht.

Die Antwort auf Ihre Frage ist ein klares Nein.

2
Matt Quigley

Alle Methoden und Variablen können in Unterklassen standardmäßig überschrieben werden. Wenn wir die Unterklassen vor dem Überschreiben der Mitglieder der Superklasse schützen möchten, können wir sie mit dem Schlüsselwort final als final deklarieren. Zum Beispiel: final int a=10;final void display(){......} Wenn Sie eine Methode finalisieren, wird sichergestellt, dass die in der Superklasse definierte Funktionalität sowieso nie geändert wird. Ebenso kann der Wert einer endgültigen Variablen niemals geändert werden. Endvariablen verhalten sich wie Klassenvariablen.

1

Ich ziehe es nur vor, lokale Variablen als final zu deklarieren, wenn:

  • I have, um sie endgültig zu machen, damit sie mit einer anonymen Klasse geteilt werden können (zum Beispiel: Erstellen eines Daemon-Threads und Zugreifen auf einen Wert aus der einschließenden Methode)

  • Ich will um sie endgültig zu machen (zum Beispiel: ein Wert, der nicht/nicht versehentlich überschrieben werden sollte)

Helfen sie bei der schnellen Müllabfuhr?
AFAIK Ein Objekt wird ein Kandidat für die GC-Sammlung, wenn es keine starken Verweise darauf aufweist. In diesem Fall kann auch nicht garantiert werden, dass es sofort mit Müll entsorgt wird. Im Allgemeinen gilt eine starke Referenz als ungültig, wenn sie den Gültigkeitsbereich verlässt, oder der Benutzer weist sie explizit der Nullreferenz zu. Wenn Sie sie als endgültig deklarieren, bedeutet dies, dass die Referenz weiterhin vorhanden ist, bis die Methode vorhanden ist (es sei denn, der Gültigkeitsbereich wird explizit auf eingeschränkt ein bestimmter innerer Block {}), da Sie die endgültigen Variablen nicht neu zuweisen können. Also denke ich w.r.t Garbage Collection 'final' kann eine unerwünschte mögliche Verzögerung einführen.

0
sactiw

Das einzige, was ich mir vorstellen kann, ist, dass der Compiler die endgültigen Variablen möglicherweise wegoptimiert und sie als Konstanten in den Code einfügt, sodass Ihnen am Ende kein Speicher zugewiesen wird.

0
Sijin

solange die Lebensdauer des Objekts verkürzt wird, was sich positiv auf die Speicherverwaltung auswirkt, haben wir kürzlich die Exportfunktion mit Instanzvariablen in einem Test und einem anderen Test mit lokalen Variablen auf Methodenebene untersucht. Während des Lasttests wirft JVM beim ersten Test einen Fehler aus dem Speicher, und JVM wurde angehalten. Im zweiten Test konnte der Bericht jedoch aufgrund einer besseren Speicherverwaltung erfolgreich abgerufen werden.

0
santhosh kumar