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

Lesezeit: 8 Minuten

Benutzer-Avatar
Goran Martinic

Heute haben meine Kollegen und ich eine Diskussion über die Verwendung der final Schlüsselwort in Java, um die Garbage Collection zu verbessern.

Wenn Sie zum Beispiel eine Methode schreiben wie:

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

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?

  • es gibt zwei Dinge hier, eigentlich. 1) wenn Sie in ein lokales Methodenfeld schreiben und eine Instanz. Wenn Sie dort in eine Instanz schreiben könnte ein Vorteil sein.

    – Eugen

    18. Dezember 2019 um 12:41 Uhr

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

public class MyClass {

   public final MyOtherObject obj;

}

Jedes Mal, wenn Sie eine Instanz von MyClass erstellen, erstellen Sie einen ausgehenden Verweis auf eine MyOtherObject-Instanz, und der GC muss diesem Link folgen, um nach Live-Objekten zu suchen.

Die JVM verwendet einen Mark-Sweep-GC-Algorithmus, der alle Live-Referenzen in den GC-“Root”-Speicherorten untersuchen muss (wie alle Objekte in der aktuellen Aufrufliste). Jedes Live-Objekt wird als lebendig “markiert”, und jedes Objekt, auf das ein Live-Objekt verweist, wird ebenfalls als lebendig markiert.

Nach Abschluss der Markierungsphase durchsucht der GC den Heap, gibt Speicher für alle nicht markierten Objekte frei (und komprimiert den Speicher für die verbleibenden Live-Objekte).

Außerdem ist es wichtig zu erkennen, dass der Java-Heap-Speicher in eine „junge Generation“ und eine „alte Generation“ unterteilt ist. Alle Objekte werden zunächst in der jungen Generation (manchmal auch als „Kindergarten“ bezeichnet) vergeben. Da die meisten Objekte kurzlebig sind, ist die GC aggressiver, wenn es darum geht, den jüngsten Müll von der jungen Generation zu befreien. Wenn ein Objekt einen Sammlungszyklus der jungen Generation überlebt, wird es in die alte Generation verschoben (manchmal auch als „Tenured Generation“ bezeichnet), die weniger häufig bearbeitet wird.

Aus dem Kopf heraus sage ich also “nein, der ‘endgültige’ Modifikator hilft dem GC nicht, seine Arbeitsbelastung zu reduzieren”.

Meiner Meinung nach besteht die beste Strategie zur Optimierung Ihrer Speicherverwaltung in Java darin, falsche Referenzen so schnell wie möglich zu eliminieren. Sie könnten dies tun, indem Sie einer Objektreferenz “null” zuweisen, sobald Sie mit der Verwendung fertig sind.

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 schließenden geschweiften Klammer) am Leben bleibt, dann 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, fallen die in dieser Methode deklarierten Objekte schneller aus dem Geltungsbereich, und der GC kann den größten Teil seiner Arbeit viel effizienter erledigen junge Generation. Sie möchten nicht, dass Objekte in die ältere Generation verschoben werden, es sei denn, dies ist unbedingt erforderlich.

  • Stoff zum Nachdenken. Ich dachte immer, Inline-Code sei schneller, aber wenn jvm wenig Speicher zur Verfügung steht, wird es auch langsamer. hmmmmm…

    – WolfmanDragon

    9. Dezember 2008 um 21:33 Uhr

  • Ich vermute hier nur … aber ich gehe davon aus, dass der JIT-Compiler endgültige primitive Werte (keine Objekte) einfügen kann, um die Leistung geringfügig zu steigern. Code-Inlining hingegen kann erhebliche Optimierungen bewirken, hat aber nichts mit finalen Variablen zu tun.

    – benjismith

    10. Dezember 2008 um 0:58 Uhr

  • Es wäre dann vielleicht nicht möglich, einem bereits erstellten Endobjekt null zuzuweisen Finale statt zu helfen, könnte die Sache erschweren

    – Hernán Eche

    14. Dezember 2011 um 21:05 Uhr

  • Man könnte { } auch verwenden, um den Bereich in einer großen Methode zu begrenzen, anstatt ihn auf mehrere private Methoden aufzuteilen, die für andere Klassenmethoden möglicherweise nicht relevant sind.

    – mjs

    10. Dezember 2012 um 14:12 Uhr

  • Sie können auch lokale Variablen anstelle von Feldern verwenden, wenn dies möglich ist, um die Speicherbereinigung zu verbessern und referenzielle Bindungen zu reduzieren.

    – siv

    10. März 2014 um 9:21 Uhr

Benutzer-Avatar
Aaron

Deklarieren einer lokalen Variablen final wirkt sich nicht auf die Garbage Collection aus, es bedeutet nur, dass Sie die Variable nicht ändern können. Ihr obiges Beispiel sollte nicht kompiliert werden, während Sie die Variable ändern totalWeight die markiert wurde final. Andererseits deklariert man ein Primitiv (double Anstatt von Double) final will ermöglicht, dass diese Variable in den aufrufenden Code eingebettet wird, was zu einer Verbesserung des Arbeitsspeichers und der Leistung führen kann. Dies wird verwendet, wenn Sie mehrere haben public static final Strings in einer Klasse.

Im Allgemeinen werden der Compiler und die Laufzeit optimiert, wo es möglich ist. Es ist am besten, den Code angemessen zu schreiben und nicht zu versuchen, zu knifflig zu sein. Verwenden final wenn Sie nicht möchten, dass die Variable geändert wird. Gehen Sie davon aus, dass alle einfachen Optimierungen vom Compiler durchgeführt werden, und wenn Sie sich Sorgen um die Leistung oder den Speicherverbrauch machen, verwenden Sie einen Profiler, um das eigentliche Problem zu ermitteln.

Nein, es ist ausdrücklich nicht wahr.

Erinnere dich daran final bedeutet nicht konstant, es bedeutet 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.

Es kann eine kleine Optimierung geben, die auf dem Wissen basiert, dass die JVM die Referenz niemals ändern muss (z. B. nicht prüfen muss, ob sie sich geändert hat), aber es wäre so gering, dass Sie sich keine Sorgen machen müssen.

Final sollten als nützliche Metadaten für den Entwickler und nicht als Compiler-Optimierung betrachtet werden.

Benutzer-Avatar
Kirche

Einige Punkte zur Klärung:

  • Das Auslöschen von Referenzen sollte GC nicht helfen. Wenn dies der Fall wäre, würde dies darauf hinweisen, dass Ihre Variablen über den Geltungsbereich hinausgehen. Eine Ausnahme bildet der Fall der Objekt-Vetternwirtschaft.

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

  • Eine Variable als final zu deklarieren bedeutet, dass Sie (unter normalen Bedingungen) dieser Variablen keinen neuen Wert zuweisen können. Da final nichts über den Geltungsbereich aussagt, sagt es nichts über seine Wirkung auf GC aus.

Nun, ich weiß nichts über die Verwendung des “final”-Modifikators in diesem Fall oder seine Auswirkung auf den GC.

Aber ich kann Ihnen Folgendes sagen: Ihre Verwendung von Boxed-Werten anstelle von Primitiven (z. B. Double statt Double) wird diese Objekte auf dem Heap statt auf dem Stack allokieren und unnötigen Müll produzieren, den der GC aufräumen muss.

Ich verwende Boxed Primitives nur, wenn dies von einer vorhandenen API verlangt wird oder wenn ich nullable Primitives benötige.

  • Sie haben Recht. Ich brauchte nur ein kurzes Beispiel, um meine Frage zu erklären.

    – Goran Martinic

    20. November 2008 um 21:20 Uhr

Benutzer-Avatar
Thorbjørn Ravn Andersen

Endgültige Variablen können nach der anfänglichen Zuweisung nicht geändert werden (vom Compiler erzwungen).

Das ändert nichts am Verhalten des Müllabfuhr als solche. Die einzige Sache ist, dass diese Variablen nicht auf Null gesetzt werden können, wenn sie nicht mehr verwendet werden (was der Garbage Collection in speicherarmen Situationen helfen kann).

Sie sollten wissen, dass final dem Compiler erlaubt, Annahmen darüber zu treffen, was optimiert werden soll. Inlining-Code ohne Code, von dem bekannt ist, dass er nicht erreichbar ist.

final boolean debug = false;

......

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

Das println wird nicht in den Bytecode aufgenommen.

  • Sie haben Recht. Ich brauchte nur ein kurzes Beispiel, um meine Frage zu erklären.

    – Goran Martinic

    20. November 2008 um 21:20 Uhr

Benutzer-Avatar
Gemeinschaft

Es gibt einen nicht so bekannten Eckfall mit Generationen-Garbage-Collectors. (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 meistens nur junge Generationen berücksichtigt werden müssen. Der Wurzelort wird nach Referenzen abgetastet, und dann werden die Objekte der jungen Generation abgetastet. Während dieser häufigeren Sweeps wird kein Objekt in der alten Generation überprüft.

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

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, wenn der einzige Verweis auf das Objekt im alten Objekt beibehalten wird, es nicht markiert wird und fälschlicherweise wäre während der Sweep-Phase freigegeben.

Wie viele betonten, wirkt sich das Schlüsselwort final natürlich 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 auf den älteren Heap schafft.

Artikel:

IBM zur Garbage Collection: Geschichtein dem Hotspot-JVM und Leistung. Diese sind möglicherweise nicht mehr vollständig gültig, da sie aus dem Jahr 2003/04 stammen, aber sie geben einen leicht lesbaren Einblick in GCs.

Sonne an Optimierung der Garbage-Collection

1257160cookie-checkVerbessert die Verwendung von final für Variablen in Java die Garbage Collection?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy