Beeinflusst das Zuweisen von Objekten zu Null in Java die Speicherbereinigung?

84

nullVerbessert das Zuweisen eines nicht verwendeten Objektverweises in Java den Garbage Collection-Prozess auf messbare Weise?

Meine Erfahrung mit Java (und C #) hat mich gelehrt, dass es oft nicht intuitiv ist, die virtuelle Maschine oder den JIT-Compiler zu überlisten, aber ich habe gesehen, dass Mitarbeiter diese Methode verwenden, und ich bin gespannt, ob dies eine gute Vorgehensweise ist up oder einer dieser Voodoo-Programmier-Aberglauben?

James McMahon
quelle

Antworten:

66

Normalerweise nein.

Aber wie alle Dinge: es kommt darauf an. Der GC in Java ist heutzutage SEHR gut und alles sollte sehr kurz nach seiner Erreichbarkeit bereinigt werden. Dies erfolgt unmittelbar nach dem Verlassen einer Methode für lokale Variablen und wenn auf eine Klasseninstanz für Felder nicht mehr verwiesen wird.

Sie müssen nur explizit null setzen, wenn Sie wissen, dass ansonsten darauf verwiesen wird. Zum Beispiel ein Array, das herumgehalten wird. Möglicherweise möchten Sie die einzelnen Elemente des Arrays auf Null setzen, wenn sie nicht mehr benötigt werden.

Beispiel: Dieser Code aus ArrayList:

public E remove(int index) {
    RangeCheck(index);

    modCount++;
    E oldValue = (E) elementData[index];

    int numMoved = size - index - 1;
    if (numMoved > 0)
         System.arraycopy(elementData, index+1, elementData, index,
             numMoved);
    elementData[--size] = null; // Let gc do its work

    return oldValue;
}

Wenn Sie ein Objekt explizit auf Null setzen, wird ein Objekt nicht früher erfasst, als wenn es auf natürliche Weise den Gültigkeitsbereich verlassen hätte, solange keine Referenzen mehr vorhanden sind.

Beide:

void foo() {
   Object o = new Object();
   /// do stuff with o
}

und:

void foo() {
   Object o = new Object();
   /// do stuff with o
   o = null;
}

Sind funktional gleichwertig.

Mark Renouf
quelle
7
Möglicherweise möchten Sie auch Referenzen in Methoden auf Null setzen, die möglicherweise viel Speicher belegen oder deren Ausführung lange dauert, oder in Code in einer Swing-Anwendung, die möglicherweise lange ausgeführt wird. Bei kurzen Methoden oder kurzlebigen Objekten ist es den zusätzlichen verwirrenden Code nicht wert.
John Gardner
1
Ist es also richtig, wenn wir ein Objekt auf Null setzen, sammelt der Garbage Collector dieses Objekt sofort?
Muhammad Babar
1
Nein. Der Garbage Collector wird regelmäßig ausgeführt, um festzustellen, ob Objekte vorhanden sind, auf die keine Referenzvariablen verweisen. Wenn Sie sie auf null setzen und dann aufrufen System.gc(), wird sie entfernt (vorausgesetzt, es gibt keine anderen Verweise, die auf dieses Objekt verweisen).
JavaTechnical
2
@JavaTechnical Wie ich weiß, kann nicht garantiert werden, dass beim Aufrufen von System.gc () die Speicherbereinigung gestartet wird.
Jack
12

Nach meiner Erfahrung heben die Leute häufig Referenzen aus Paranoia auf, nicht aus Notwendigkeit. Hier ist eine kurze Richtlinie:

  1. Wenn Objekt A auf Objekt B verweist und Sie diese Referenz nicht mehr benötigen und Objekt A nicht für die Speicherbereinigung berechtigt ist, sollten Sie das Feld explizit auf Null setzen. Es ist nicht erforderlich, ein Feld auf Null zu setzen, wenn für das einschließende Objekt ohnehin Müll gesammelt wird. Das Nullstellen von Feldern in einer dispose () -Methode ist fast immer nutzlos.

  2. In einer Methode erstellte Objektreferenzen müssen nicht auf Null gesetzt werden. Sie werden automatisch gelöscht, sobald die Methode beendet wird. Die Ausnahme von dieser Regel besteht darin, dass Sie in einer sehr langen Methode oder einer massiven Schleife ausgeführt werden und sicherstellen müssen, dass einige Referenzen vor dem Ende der Methode gelöscht werden. Auch diese Fälle sind äußerst selten.

Ich würde sagen, dass Sie die meiste Zeit keine Referenzen auf Null setzen müssen. Der Versuch, den Garbage Collector auszutricksen, ist nutzlos. Sie erhalten nur ineffizienten, unlesbaren Code.

Gili
quelle
11

Guter Artikel ist der heutige Coding-Horror .

Die Arbeit von GC besteht darin, nach Objekten zu suchen, die keine Zeiger auf sie haben. Der Bereich ihrer Suche ist Heap / Stack und alle anderen Leerzeichen, die sie haben. Wenn Sie also eine Variable auf null setzen, wird das tatsächliche Objekt jetzt von niemandem mehr angezeigt und kann daher als GC bezeichnet werden.

Da der GC jedoch möglicherweise nicht genau zu diesem Zeitpunkt ausgeführt wird, kaufen Sie sich möglicherweise selbst nichts. Wenn Ihre Methode jedoch ziemlich lang ist (in Bezug auf die Ausführungszeit), lohnt es sich möglicherweise, da Sie Ihre Chancen erhöhen, dass GC dieses Objekt sammelt.

Das Problem kann auch bei Codeoptimierungen kompliziert sein. Wenn Sie die Variable nie verwenden, nachdem Sie sie auf null gesetzt haben, ist es eine sichere Optimierung, die Zeile zu entfernen, in der der Wert auf null gesetzt wird (eine auszuführende Anweisung weniger). Sie erhalten also möglicherweise keine Verbesserung.

Zusammenfassend kann es also helfen, aber es wird nicht deterministisch sein .

EarlNameless
quelle
Guter Punkt für die sichere Optimierung, bei der die Zeile entfernt wird, in der der Verweis auf Null gesetzt wird.
Asgs
8

Zumindest in Java ist es überhaupt keine Voodoo-Programmierung. Wenn Sie ein Objekt in Java mit so etwas wie erstellen

Foo bar = new Foo();

Sie machen zwei Dinge: Erstens erstellen Sie einen Verweis auf ein Objekt und zweitens erstellen Sie das Foo-Objekt selbst. Solange diese oder eine andere Referenz existiert, kann das spezifische Objekt nicht gc'd werden. Wenn Sie dieser Referenz jedoch null zuweisen ...

bar = null ;

und vorausgesetzt, nichts anderes hat einen Verweis auf das Objekt, ist es freigegeben und für gc verfügbar, wenn der Garbage Collector das nächste Mal vorbeikommt.

Charlie Martin
quelle
11
Das Verlassen des Gültigkeitsbereichs führt jedoch auch ohne die zusätzliche Codezeile aus. Wenn es sich um eine lokale Methode handelt, besteht keine Notwendigkeit. Sobald Sie die Methode verlassen, ist das Objekt für die GC berechtigt.
Duffymo
2
Gut. Erstellen Sie jetzt für ein Pop-Quiz ein Beispiel für Code, für den dies NICHT ausreicht. Hinweis: Sie existieren.
Charlie Martin
7

Es hängt davon ab, ob.

Im Allgemeinen halten Sie Verweise auf Ihre Objekte kürzer, je schneller sie gesammelt werden.

Wenn die Ausführung Ihrer Methode beispielsweise 2 Sekunden dauert und Sie nach einer Sekunde der Methodenausführung kein Objekt mehr benötigen, ist es sinnvoll, alle Verweise darauf zu löschen. Wenn GC nach einer Sekunde feststellt, dass auf Ihr Objekt immer noch verwiesen wird, wird es beim nächsten Mal möglicherweise in etwa einer Minute überprüft.

Jedenfalls ist es für mich eine vorzeitige Optimierung, alle Verweise standardmäßig auf Null zu setzen, und niemand sollte dies tun, es sei denn, in bestimmten seltenen Fällen wird der Speicherverbrauch messbar verringert.

lubos hasko
quelle
6

Das explizite Festlegen eines Verweises auf null, anstatt nur die Variable aus dem Gültigkeitsbereich zu entfernen, hilft dem Garbage Collector nicht, es sei denn, das gehaltene Objekt ist sehr groß. Es ist eine gute Idee, es auf null zu setzen, sobald Sie damit fertig sind.

Wenn Sie Verweise im Allgemeinen auf null setzen, bedeutet dies für den LESER des Codes, mit dem dieses Objekt vollständig fertig ist, und sollte sich nicht mehr darum kümmern.

Ein ähnlicher Effekt kann erzielt werden, indem ein engerer Bereich eingeführt wird, indem ein zusätzlicher Satz Zahnspangen eingesetzt wird

{
  int l;
  {  // <- here
    String bigThing = ....;
    l = bigThing.length();
  }  // <- and here
}

Auf diese Weise kann das bigThing direkt nach dem Verlassen der verschachtelten Klammern als Müll gesammelt werden.

Thorbjørn Ravn Andersen
quelle
Gute Alternative. Dadurch wird vermieden, dass die Variable explizit auf Null gesetzt wird und die Flusenwarnung, die einige IDEs anzeigen, dass diese Nullzuweisung nicht verwendet wird.
jk7
5
public class JavaMemory {
    private final int dataSize = (int) (Runtime.getRuntime().maxMemory() * 0.6);

    public void f() {
        {
            byte[] data = new byte[dataSize];
            //data = null;
        }

        byte[] data2 = new byte[dataSize];
    }

    public static void main(String[] args) {

        JavaMemory jmp = new JavaMemory();
        jmp.f();

    }

}

Über Programm wirft OutOfMemoryError. Wenn Sie auskommentieren data = null;, OutOfMemoryErrorist das gelöst. Es wird immer empfohlen, die nicht verwendete Variable auf null zu setzen

Omiel
quelle
Das ist imo ein solider Ausflug in die Welt der Strohmann-Argumente. Nur weil Sie eine Situation erstellen können, in der das Setzen der Variablen auf Null das Problem in diesem speziellen Fall löst (und ich bin nicht davon überzeugt, dass Sie dies getan haben), bedeutet dies nicht, dass das Setzen auf Null in jedem Fall eine gute Idee ist. Wenn Sie den Code hinzufügen, um jede Variable, die Sie nicht mehr verwenden, auf null zu setzen, selbst wenn sie kurz danach den Gültigkeitsbereich verlassen, wird der Code nur noch aufgebläht und der Code wird weniger wartbar.
RHSeeger
Warum wird der Müll des Byte [] -Arrays nicht erfasst, wenn die variablen Daten außerhalb des Gültigkeitsbereichs liegen?
Grav
2
@Grav: Das Verlassen des Gültigkeitsbereichs garantiert nicht, dass der Garbage Collector die lokalen Objekte sammelt. Wenn Sie es jedoch auf null setzen, wird die OutOfMemory-Ausnahme verhindert, da Sie garantiert sind, dass der GC ausgeführt wird, bevor Sie eine OutOfMemory-Ausnahme auslösen. Wenn das Objekt auf null gesetzt wurde, kann es gesammelt werden.
Stefanos T.
4

Ich habe einmal an einer Videokonferenzanwendung gearbeitet und einen großen Leistungsunterschied festgestellt, als ich mir die Zeit nahm, Referenzen auf Null zu setzen, sobald ich das Objekt nicht mehr brauchte. Dies war in den Jahren 2003-2004 und ich kann mir nur vorstellen, dass der GC seitdem noch schlauer geworden ist. In meinem Fall kamen und gingen jede Sekunde Hunderte von Objekten aus dem Geltungsbereich, sodass ich den GC bemerkte, als er regelmäßig einschaltete. Nachdem ich jedoch darauf hingewiesen hatte, Objekte auf Null zu setzen, hörte der GC auf, meine Anwendung anzuhalten.

Es kommt also darauf an, was du tust ...

Perry
quelle
2

Ja.

Aus "The Pragmatic Programmer" S.292:

Indem Sie einen Verweis auf NULL setzen, reduzieren Sie die Anzahl der Zeiger auf das Objekt um eins ... (wodurch der Garbage Collector es entfernen kann).


quelle
Ich denke, dies gilt nur, wenn die Referenzanzahl algo. wird verwendet oder anderweitig auch?
Nrj
2
Dieser Rat ist für jeden modernen Müllsammler überholt. und Referenzzählung GC hat signifikante Probleme, wie z. B. Zirkelreferenzen.
Lawrence Dol
Es wäre auch wahr in einem Mark and Sweep GC; wahrscheinlich in allen anderen. Die Tatsache, dass Sie eine Referenz weniger haben, bedeutet nicht, dass die Referenz gezählt wird. Die Antwort von tweakt erklärt dies. Es ist besser, den Umfang zu reduzieren, als auf NULL zu setzen.
1

Ich gehe davon aus, dass sich das OP auf solche Dinge bezieht:

private void Blah()
{
    MyObj a;
    MyObj b;

    try {
        a = new MyObj();
        b = new MyObj;

        // do real work
    } finally {
        a = null;
        b = null;
    }
}

Würde die VM sie in diesem Fall nicht für GC markieren, sobald sie den Gültigkeitsbereich verlassen?

Oder würde aus einer anderen Perspektive das explizite Setzen der Elemente auf Null dazu führen, dass sie eine GC-Prüfung erhalten, bevor sie dies tun würden, wenn sie nur den Gültigkeitsbereich verlassen würden? In diesem Fall verbringt die VM möglicherweise Zeit mit der GC-Prüfung des Objekts, wenn der Speicher ohnehin nicht benötigt wird. Dies würde tatsächlich zu einer schlechteren CPU-Auslastung führen, da die GC früher ausgeführt wird.

CodingWithSpike
quelle
Der Zeitpunkt, zu dem der GC ausgeführt wird, ist nicht deterministisch. Ich glaube nicht, dass das Setzen eines Objekts nulldas GC-Verhalten überhaupt beeinflusst.
Harto
0

" Es kommt darauf an "

Ich kenne Java nicht, aber in .net (C #, VB.net ...) ist es normalerweise nicht erforderlich, eine Null zuzuweisen, wenn Sie kein Objekt mehr benötigen.

Beachten Sie jedoch, dass dies "normalerweise nicht erforderlich" ist.

Durch die Analyse Ihres Codes bewertet der .net-Compiler die Lebensdauer der Variablen gut ... um genau zu erkennen, wann das Objekt nicht mehr verwendet wird. Wenn Sie also obj = null schreiben, sieht es möglicherweise so aus, als ob das obj noch verwendet wird. In diesem Fall ist es kontraproduktiv, eine Null zuzuweisen.

Es gibt einige Fälle, in denen es tatsächlich hilfreich sein kann, eine Null zuzuweisen. Ein Beispiel ist, dass Sie einen riesigen Code haben, der lange ausgeführt wird, oder eine Methode, die in einem anderen Thread oder einer Schleife ausgeführt wird. In solchen Fällen kann es hilfreich sein, null zuzuweisen, damit der GC leicht erkennen kann, dass er nicht mehr verwendet wird.

Hierfür gibt es keine feste Regel. Gehen Sie an der obigen Stelle vorbei und weisen Sie Ihrem Code Null-Zuweisungen zu. Führen Sie einen Profiler aus, um festzustellen, ob dies in irgendeiner Weise hilfreich ist. Höchstwahrscheinlich sehen Sie keinen Nutzen.

Wenn es sich um .net-Code handelt, den Sie optimieren möchten, ist es meiner Erfahrung nach vorteilhafter, mit Dispose- und Finalize-Methoden vorsichtig umzugehen, als sich um Nullen zu kümmern.

Einige Referenzen zum Thema:

http://blogs.msdn.com/csharpfaq/archive/2004/03/26/97229.aspx

http://weblogs.asp.net/pwilson/archive/2004/02/20/77422.aspx

Sesh
quelle
0

Selbst wenn das Aufheben der Referenz geringfügig effizienter wäre, wäre es die Hässlichkeit wert, Ihren Code mit diesen hässlichen Aufhebungen pfeffern zu müssen? Sie wären nur unübersichtlich und verdecken den Intent-Code, der sie enthält.

Es ist eine seltene Codebasis, die keinen besseren Kandidaten für eine Optimierung hat als den Versuch, den Garbage Collector auszutricksen (noch seltener sind Entwickler, denen es gelingt, ihn auszutricksen). Ihre Bemühungen werden höchstwahrscheinlich besser woanders eingesetzt, indem Sie diesen mürrischen XML-Parser fallen lassen oder eine Gelegenheit finden, die Berechnung zwischenzuspeichern. Diese Optimierungen sind einfacher zu quantifizieren und erfordern nicht, dass Sie Ihre Codebasis mit Rauschen verschmutzen.

Sgargan
quelle
0

Bei der zukünftigen Ausführung Ihres Programms werden die Werte einiger Datenelemente verwendet, um eine außerhalb des Programms sichtbare Ausgabe zu berechnen. Andere können verwendet werden oder nicht, abhängig von zukünftigen (und nicht vorhersehbaren) Eingaben in das Programm. Andere Datenelemente werden möglicherweise nicht verwendet. Alle Ressourcen, einschließlich des Speichers, die diesen nicht verwendeten Daten zugewiesen sind, werden verschwendet. Die Aufgabe des Garbage Collector (GC) besteht darin, diesen verschwendeten Speicher zu beseitigen. Es wäre katastrophal für den GC, etwas zu eliminieren, das benötigt wird, daher könnte der verwendete Algorithmus konservativ sein und mehr als das strenge Minimum beibehalten. Möglicherweise werden heuristische Optimierungen verwendet, um die Geschwindigkeit zu verbessern, und zwar auf Kosten der Beibehaltung einiger Elemente, die nicht tatsächlich benötigt werden. Es gibt viele mögliche Algorithmen, die der GC verwenden könnte. Daher ist es möglich, dass Sie Änderungen an Ihrem Programm vornehmen, und die die Richtigkeit Ihres Programms nicht beeinträchtigen, können sich dennoch auf den Betrieb des GC auswirken, indem er entweder schneller ausgeführt wird, um denselben Job auszuführen, oder nicht verwendete Elemente früher identifiziert. Also diese Art von Änderung, auf die ein ungewöhnlicher Objektverweis gesetzt wirdnull, In der Theorie ist nicht immer Voodoo.

Ist es Voodoo? Es gibt angeblich Teile des Java-Bibliothekscodes, die dies tun. Die Verfasser dieses Codes sind viel besser als durchschnittliche Programmierer und kennen Programmierer, die Details der Garbage Collector-Implementierungen kennen, oder arbeiten mit ihnen zusammen. Das deutet darauf hin, dass es manchmal einen Vorteil gibt.

Raedwald
quelle
0

Wie Sie sagten, gibt es Optimierungen, dh JVM kennt den Ort, an dem die Variable zuletzt verwendet wurde, und das Objekt, auf das sie verweist, kann direkt nach diesem letzten Punkt überprüft werden (wird immer noch im aktuellen Bereich ausgeführt). Das Aufheben von Referenzen in den meisten Fällen hilft GC also nicht.

Es kann jedoch nützlich sein, das Problem "Vetternwirtschaft" (oder "schwimmender Müll") zu vermeiden ( lesen Sie hier mehr oder sehen Sie sich ein Video an ). Das Problem besteht darin, dass der Heap in alte und junge Generationen aufgeteilt ist und verschiedene GC-Mechanismen angewendet werden: Minor GC (was schnell ist und häufig junge Gen reinigt) und Major Gc (was zu einer längeren Pause führt, um Old Gen zu reinigen). "Nepotismus" erlaubt nicht, dass Müll in der jungen Generation gesammelt wird, wenn er durch Müll referenziert wird, der bereits zu einer alten Generation gehört.

Dies ist "pathologisch", da JEDER heraufgestufte Knoten zur Heraufstufung ALLER folgenden Knoten führt, bis ein GC das Problem behebt.

Um Vetternwirtschaft zu vermeiden, ist es eine gute Idee, Referenzen von einem Objekt, das entfernt werden soll, auf Null zu setzen. Sie können diese Technik in JDK-Klassen anwenden : LinkedList und LinkedHashMap

private E unlinkFirst(Node<E> f) {
    final E element = f.item;
    final Node<E> next = f.next;
    f.item = null;
    f.next = null; // help GC
    // ...
}
Kirill
quelle