Heute diskutieren meine Kollegen und ich über die Verwendung des final
Schlüsselworts in Java, um die Speicherbereinigung 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 nach dem Beenden der Methode von den nicht verwendeten Variablen in der Methode zu bereinigen.
Ist das wahr?
java
garbage-collection
final
Goran Martinic
quelle
quelle
Antworten:
Hier ist ein etwas anderes Beispiel, eines mit Feldern vom endgültigen Referenztyp anstelle von lokalen Variablen vom endgültigen Werttyp:
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 an den GC- "Root" -Standorten untersuchen muss (wie alle Objekte im aktuellen Aufrufstapel). Jedes lebende Objekt wird als lebendig "markiert", und jedes Objekt, auf das sich ein lebendes Objekt bezieht, wird ebenfalls als lebendig markiert.
Nach Abschluss der Markierungsphase durchläuft der GC den Heap und gibt Speicher für alle nicht markierten Objekte frei (und komprimiert den Speicher für die verbleibenden Live-Objekte).
Es ist auch wichtig zu erkennen, dass der Java-Heapspeicher in eine "junge Generation" und eine "alte Generation" unterteilt ist. Alle Objekte werden zunächst in der jungen Generation zugeordnet (manchmal auch als "Kindergarten" bezeichnet). Da die meisten Objekte nur von kurzer Dauer sind, ist der 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 (manchmal als "Tenured Generation" bezeichnet) verschoben, die weniger häufig verarbeitet wird.
Ich werde also auf den ersten Blick sagen: "Nein, der 'letzte' 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 beseitigen. Sie können dies tun, indem Sie einer Objektreferenz "null" zuweisen, sobald Sie damit 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 Bereichs dieser Methode (der letzten schließenden 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, fallen die in dieser Methode deklarierten Objekte schneller aus dem Geltungsbereich und der GC kann den größten Teil seiner Arbeit innerhalb der wesentlich effizienteren erledigen junge Generation. Sie möchten nicht, dass Objekte in die ältere Generation verschoben werden, es sei denn, dies ist unbedingt erforderlich.
quelle
Das Deklarieren einer lokalen Variablen
final
hat keine Auswirkungen auf die Speicherbereinigung. Dies bedeutet nur, dass Sie die Variable nicht ändern können. Ihr Beispiel oben sollte nicht kompiliert werden, da SietotalWeight
die markierte Variable ändernfinal
. Wenn Sie jedoch ein Grundelement (double
anstelle vonDouble
) deklarieren,final
kann diese Variable in den aufrufenden Code eingefügt werden, was zu einer Verbesserung des Speichers und der Leistung führen kann. Dies wird verwendet, wenn Sie eine Anzahl vonpublic static final Strings
in einer Klasse haben.Im Allgemeinen optimieren der Compiler und die Laufzeit, wo dies möglich ist. Es ist am besten, den Code entsprechend zu schreiben und nicht zu knifflig zu sein. Verwenden
final
Sie diese Option, 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, verwenden Sie einen Profiler, um das eigentliche Problem zu ermitteln.quelle
Nein, es ist ausdrücklich nicht wahr.
Denken Sie daran, dass
final
dies keine Konstante bedeutet, sondern nur, dass Sie die Referenz nicht ändern können.Es kann einige kleine Optimierungen geben, die auf dem Wissen beruhen, dass die JVM die Referenz niemals ändern muss (z. B. keine Überprüfung, ob sie sich geändert hat), aber sie wäre so geringfügig, dass sie sich keine Sorgen macht.
Final
sollte als nützliche Metadaten für den Entwickler und nicht als Compileroptimierung betrachtet werden.quelle
Einige Punkte, die geklärt werden müssen:
Das Nullstellen der Referenz sollte GC nicht helfen. Wenn dies der Fall wäre, würde dies darauf hinweisen, dass Ihre Variablen einen übermäßigen Gültigkeitsbereich haben. Eine Ausnahme bildet der Fall von Objektvetternwirtschaft.
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 die Auswirkungen auf die GC aus.
quelle
Nun, ich weiß nicht, wie der "letzte" Modifikator in diesem Fall verwendet wird oder wie er sich auf den GC auswirkt.
Aber ich kann Ihnen Folgendes sagen: Ihre Verwendung von Boxed-Werten anstelle von Grundelementen (z. B. Double anstelle von Double) weist diese Objekte eher auf dem Heap als auf dem Stapel zu und erzeugt unnötigen Müll, den der GC bereinigen muss.
Ich verwende Boxed-Primitive nur, wenn dies für eine vorhandene API erforderlich ist oder wenn ich nullfähige Primitive benötige.
quelle
Endgültige Variablen können nach der ersten Zuweisung nicht mehr geändert werden (vom Compiler erzwungen).
Dies ändert nichts am Verhalten der Garbage Collection als solcher. Das einzige ist, dass diese Variablen nicht auf Null gesetzt werden können, wenn sie nicht mehr verwendet werden (was der Speicherbereinigung in speichersicheren Situationen helfen kann).
Sie sollten wissen, dass der Compiler mit final Annahmen darüber treffen kann, was optimiert werden soll. Inlining-Code und ohne Code, von dem bekannt ist, dass er nicht erreichbar ist.
Der Ausdruck wird nicht in den Bytecode aufgenommen.
quelle
Es gibt einen nicht so bekannten Eckfall mit Müllsammlern der Generation. (Für eine kurze Beschreibung lesen Sie die Antwort von Benjaminism für einen tieferen Einblick lesen Sie die Artikel am Ende).
Die Idee in Generations-GCs ist, dass meistens nur junge Generationen berücksichtigt werden müssen. Der Stammort wird nach Referenzen durchsucht, und dann werden die Objekte der jungen Generation gescannt. Während dieser häufigeren Sweeps wird kein Objekt der alten Generation überprüft.
Das Problem ergibt sich nun aus der Tatsache, 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 vom Garbage Collector explizit verfolgt werden (siehe Artikel von IBM zum Hotspot-JVM-Collector ), was sich tatsächlich auf die GC-Leistung auswirkt.
Der Grund, warum ein altes Objekt nicht auf ein jüngeres Objekt verweisen kann, besteht darin, dass das alte Objekt, da es in kleineren Sammlungen nicht überprüft wird, nicht markiert wird und falsch ist, wenn der einzige Verweis auf das Objekt im alten Objekt beibehalten wird während der Sweep-Phase freigegeben.
Wie viele darauf hinweisen, wirkt sich das endgültige Schlüsselwort natürlich nicht wirklich auf den Garbage Collector aus, garantiert jedoch, dass die Referenz niemals in ein jüngeres Objekt geändert wird, wenn dieses Objekt die kleineren Sammlungen überlebt und auf den älteren Heap gelangt.
Artikel:
IBM zur Speicherbereinigung: Verlauf , Hotspot-JVM und Leistung . Diese sind möglicherweise nicht mehr vollständig gültig, da sie aus dem Jahr 2003/04 stammen, bieten jedoch einen leicht lesbaren Einblick in GCs.
Sun on Tuning Müllabfuhr
quelle
GC wirkt auf nicht erreichbare Refs. Dies hat nichts mit "final" zu tun, was lediglich eine Behauptung einer einmaligen Zuordnung ist. Ist es möglich, dass der GC einiger VMs "final" verwenden kann? Ich verstehe nicht wie oder warum.
quelle
final
Informationen zu lokalen Variablen und Parametern haben keinen Einfluss auf die erstellten Klassendateien und können daher die Laufzeitleistung nicht beeinträchtigen. 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, dassfinal
Methoden den Klassen sehr ähnlich sind.final
Im statischen Feld kann die Variable möglicherweise als "Konstante zur Kompilierungszeit" interpretiert und auf dieser Basis von javac optimiert werden.final
Auf Feldern kann die JVM einige Freiheiten ignorieren , bevor Beziehungen auftreten.quelle
Es scheint viele Antworten zu geben, die wandernde Vermutungen sind. Die Wahrheit ist, dass es keinen endgültigen Modifikator für lokale Variablen auf Bytecode-Ebene gibt. Die virtuelle Maschine wird niemals erfahren, dass Ihre lokalen Variablen als endgültig definiert wurden oder nicht.
Die Antwort auf Ihre Frage ist ein klares Nein.
quelle
final int x; if (cond) x=1; else x=2;
). Der Compiler kann daher ohne das Schlüsselwort final garantieren, dass eine Variable endgültig ist oder nicht.Alle Methoden und Variablen können standardmäßig in Unterklassen überschrieben werden. Wenn wir die Unterklassen vor dem Überschreiben der Mitglieder der Oberklasse schützen möchten, können wir sie mit dem Schlüsselwort final als final deklarieren. Wenn Sie beispielsweise
final int a=10;
final void display(){......}
eine Methode endgültig festlegen, wird sichergestellt, dass die in der Oberklasse definierte Funktionalität ohnehin nie geändert wird. Ebenso kann der Wert einer endgültigen Variablen niemals geändert werden. Endvariablen verhalten sich wie Klassenvariablen.quelle
Genau genommen über Instanz Felder,
final
könnte die Leistung etwas verbessern , wenn eine bestimmte GC will das ausnutzen. Wenn ein gleichzeitigerGC
Vorgang stattfindet (das bedeutet, dass Ihre Anwendung noch ausgeführt wird, während der GC ausgeführt wird ), finden Sie hier eine ausführlichere Erklärung . GCs müssen bestimmte Barrieren anwenden, wenn Schreib- und / oder Lesevorgänge ausgeführt werden. Der Link, den ich Ihnen gegeben habe, erklärt das ziemlich genau, aber um es wirklich kurz zu machen: Wenn aGC
gleichzeitig arbeitet, werden alle Lese- und Schreibvorgänge auf dem Heap (während dieser GC ausgeführt wird) "abgefangen" und später angewendet. damit die gleichzeitige GC-Phase ihre Arbeit beenden kann.Zum
final
Beispiel können Felder weggelassen werden, da sie nicht geändert werden können (außer Reflexion). Und das ist nicht nur reine Theorie.Shenandoah GC
hat sie in der Praxis (wenn auch nicht lange ), und Sie können zum Beispiel tun:Und es wird Optimierungen im GC-Algorithmus geben, die ihn etwas schneller machen. Dies liegt daran, dass es keine Hindernisse geben wird, die abfangen
final
, da niemand sie jemals modifizieren sollte. Nicht einmal über Reflexion oder JNI.quelle
Das einzige, was mir einfällt, ist, dass der Compiler die endgültigen Variablen möglicherweise optimiert und sie als Konstanten in den Code einfügt, sodass Ihnen am Ende kein Speicher zugewiesen wird.
quelle
Absolut, solange die Lebensdauer des Objekts verkürzt wird, was einen großen Vorteil der Speicherverwaltung bietet, haben wir kürzlich die Exportfunktionalität mit Instanzvariablen für einen Test und einem anderen Test mit lokaler Variable auf Methodenebene untersucht. Während des Lasttests wirft JVM beim ersten Test einen Speicherfehler aus und JVM wurde angehalten. Im zweiten Test konnte der Bericht jedoch aufgrund einer besseren Speicherverwaltung erfolgreich abgerufen werden.
quelle
Ich ziehe es vor, lokale Variablen als endgültig zu deklarieren, wenn:
Ich muss sie endgültig machen, damit sie für eine anonyme Klasse freigegeben werden können (zum Beispiel: Erstellen eines Daemon-Threads und Ermöglichen des Zugriffs auf einen Wert aus der eingeschlossenen Methode)
Ich möchte sie endgültig machen (zum Beispiel: ein Wert, der nicht versehentlich überschrieben werden sollte / wird)
quelle