Funktioniert das Setzen von Java-Objekten auf Null nicht mehr?

112

Ich habe einige alte Bücher durchgesehen und eine Kopie von "Practical Java" von Peter Hagger gefunden. Im Leistungsabschnitt gibt es eine Empfehlung, Objektreferenzen festzulegen, auf die nullnicht mehr benötigt wird.

Werden in Java Objektreferenzen festgelegt, nullum die Leistung oder die Effizienz der Speicherbereinigung zu verbessern? Wenn ja, in welchen Fällen ist dies ein Problem? Containerklassen? Objektzusammensetzung? Anonyme innere Klassen?

Ich sehe das ziemlich oft im Code. Ist dies jetzt veraltete Programmierempfehlung oder ist es immer noch nützlich?

sal
quelle
2
Profiliere es. In modernen Laufzeiten sollten Sie keine signifikante Steigerung der Leistung oder des Speicherbedarfs feststellen.
Jason Coco
12
@ Jason, Profil es? Dies setzt voraus, dass ich eine ausreichend große Anzahl von Fällen profiliere, um eine ausreichend gute Ergebnismenge zu erhalten, um dies zu beantworten. Und dass ich keine Fälle auswähle, in denen die VM so optimiert ist, dass sie die GC- und Leistungsprobleme maskiert. Deshalb frage ich das hier. Um einen Eindruck von den Fällen zu bekommen, in denen dies ein Problem ist.
Sal
1
Duplikat von stackoverflow.com/questions/449409/… .
Michael Myers

Antworten:

73

Es hängt ein wenig davon ab, wann Sie daran gedacht haben, die Referenz auf Null zu setzen.

Wenn Sie eine Objektkette A-> B-> C haben, können A, B und C, sobald A nicht erreichbar ist, alle zur Speicherbereinigung berechtigt sein (vorausgesetzt, nichts anderes bezieht sich auf B oder C). Es besteht keine Notwendigkeit und war auch nie eine Notwendigkeit, die Referenzen A-> B oder B-> C beispielsweise explizit auf Null zu setzen.

Abgesehen davon tritt das Problem meistens nicht wirklich auf, da es sich in Wirklichkeit um Objekte in Sammlungen handelt. Sie sollten im Allgemeinen immer daran denken, Objekte aus Listen, Karten usw. zu entfernen, indem Sie die entsprechende remove () -Methode aufrufen.

Der Fall, in dem es früher einige Ratschläge gab, Verweise auf Null zu setzen, war speziell in einem langen Bereich, in dem ein speicherintensives Objekt während des gesamten Bereichs nicht mehr verwendet wurde . Beispielsweise:

{
  BigObject obj = ...
  doSomethingWith(obj);
  obj = null;             <-- explicitly set to null
  doSomethingElse();
}

Der Grund hierfür war, dass obj , da es sich noch im Gültigkeitsbereich befindet, ohne die explizite Nullung der Referenz erst nach Abschluss der Methode doSomethingElse () zur Speicherbereinigung wird . Und dies ist der Rat, den moderne JVMs wahrscheinlich nicht mehr haben : Es stellt sich heraus, dass der JIT-Compiler herausfinden kann, an welchem ​​Punkt eine bestimmte lokale Objektreferenz nicht mehr verwendet wird.

Neil Coffey
quelle
2
Lokale Variablen können von der Maschine optimiert werden. Klassenvariablen können nicht. Ich habe gesehen, dass Worker-Threads (in einem Thread-Pool) ihre Statusobjekte nach der Verarbeitung der Anforderung nicht freigeben und diese Objekte daher im Speicher fixieren, bis dem Worker-Thread eine neue Aufgabe zugewiesen wurde (die die alten Statusobjekte sofort mit neuen überschrieb). Bei großen Statusobjekten und Hunderten von Arbeitsthreads (denken Sie an einen großen http-Server) kann dies eine enorme Menge an Speicher sein, die gespeichert und kopiert wird, aber nie wieder verwendet wird.
Brian White
1
Ich sollte wahrscheinlich nur hinzufügen: Der Rat, den ich oben gebe, ist der Rat, den ich allgemein befolgen muss, vorausgesetzt, dass sich Bibliotheken mit gutem Verhalten, eine VM mit gutem Verhalten usw. befinden. Wenn Sie feststellen, dass Sie beispielsweise eine Thread-Pool-Bibliothek haben, die an Objekten nachhängt Ein Job ist beendet, na ja ... das ist ein Fehler in der Thread-Pool-Bibliothek, der möglicherweise durch Nullstellen einer Objektreferenz umgangen werden könnte, aber auch durch Verwenden einer weniger fehlerhaften Thread-Pool-Bibliothek. Ich bin mir nicht sicher, ob ein solcher Fehler die allgemeinen Designprinzipien ändert.
Neil Coffey
25

Nein, es ist kein veralteter Rat. Baumelnde Referenzen sind immer noch ein Problem, insbesondere wenn Sie beispielsweise einen erweiterbaren Array-Container ( ArrayListoder dergleichen) mithilfe eines vorab zugewiesenen Arrays implementieren . Elemente, die über die "logische" Größe der Liste hinausgehen, sollten auf Null gesetzt werden, da sie sonst nicht freigegeben werden.

Siehe Effective Java 2nd ed, Punkt 6: Veraltete Objektreferenzen beseitigen.

Chris Jester-Young
quelle
Ist dies ein Sprachproblem oder ein VM-Implementierungsproblem?
Pablo Santa Cruz
4
Es ist ein "semantisches" Problem. Grundsätzlich sieht die VM dies, weil Sie ein Array vorab zugewiesen haben. Es weiß nichts über die "logische" Größe Ihres Containers. Angenommen, Sie haben eine ArrayList der Größe 10, die von einem Array mit 16 Elementen unterstützt wird. Die VM kann nicht wissen, dass die Elemente 10..15 nicht tatsächlich verwendet werden. Wenn diese Slots Inhalt haben, werden sie nicht freigegeben.
Chris Jester-Young
Was ist außerhalb von Containerklassen? In der Objektzusammensetzung, in der das innere Objekt nicht vom äußeren Objekt aufgehoben wird, ist.
Sal
7
@sal Die allgemeine Faustregel lautet: Wenn Sie nicht darauf verweisen können, wird Müll gesammelt. Wenn das äußere Objekt also einen Verweis auf ein anderes Objekt enthält und davon ausgegangen wird, dass das innere Objekt keine anderen Verweise hat, führt das Setzen des einzigen Verweises des äußeren Objekts auf null dazu, dass das gesamte Objekt einschließlich seiner verwaisten Verweise in Müll gesammelt wird.
Übermensch
2
In demselben Punkt 6, den Sie erwähnt haben: Das Nullstellen von Objektreferenzen sollte eher die Ausnahme als die Norm sein.
ACV
10

Instanzfelder, Array-Elemente

Wenn auf ein Objekt verwiesen wird, kann kein Müll gesammelt werden. Insbesondere wenn dieses Objekt (und das gesamte Diagramm dahinter) groß ist, gibt es nur eine Referenz, die die Speicherbereinigung stoppt, und diese Referenz wird nicht mehr wirklich benötigt. Dies ist eine unglückliche Situation.

Pathologische Fälle sind das Objekt, das eine unnötige Instanz für den gesamten XML-DOM-Baum beibehält, der zum Konfigurieren verwendet wurde, die MBean, die nicht abgemeldet wurde, oder der einzelne Verweis auf ein Objekt aus einer nicht bereitgestellten Webanwendung, der verhindert, dass ein ganzer Klassenladeprogramm entladen wird .

Wenn Sie also nicht sicher sind, dass das Objekt, das die Referenz selbst enthält, ohnehin (oder sogar dann) Müll gesammelt wird, sollten Sie alles auf Null setzen, was Sie nicht mehr benötigen.

Gültigkeitsbereichsvariablen:

Wenn Sie erwägen, eine lokale Variable vor dem Ende ihres Gültigkeitsbereichs auf null zu setzen, damit sie vom Garbage Collector zurückgefordert und als "von nun an unbrauchbar" markiert werden kann, sollten Sie sie stattdessen in einen eingeschränkteren Gültigkeitsbereich verschieben .

{
  BigObject obj = ...
  doSomethingWith(obj);
  obj = null;          //   <-- explicitly set to null
  doSomethingElse();
}

wird

{
  {  
     BigObject obj = ...
     doSomethingWith(obj);
  }    //         <-- obj goes out of scope
  doSomethingElse();
}

Lange, flache Bereiche sind im Allgemeinen auch für die Lesbarkeit des Codes schlecht. Die Einführung privater Methoden, um Dinge nur für diesen Zweck aufzubrechen, ist ebenfalls keine Seltenheit.

Thilo
quelle
Wenn Sie sich auf eine starke Referenz beziehen, ist dies zwar der Fall, jedoch nicht für alle Referenzen. Schwache Referenzen in Java können Müll gesammelt werden.
James Drinkard
5

In speicherbeschränkenden Umgebungen (z. B. Mobiltelefonen) kann dies nützlich sein. Wenn Sie null setzen, muss der objetc nicht auf die Variable warten, um den Bereich zu verlassen, der gc'd werden soll.

Für die alltägliche Programmierung sollte dies jedoch nicht die Regel sein, außer in besonderen Fällen wie dem von Chris Jester-Young genannten.

besen
quelle
1

Erstens bedeutet es nichts, auf das Sie ein Objekt setzen null. Ich erkläre es unten:

List list1 = new ArrayList();
List list2 = list1;

Im obigen Codesegment erstellen wir den Namen list1der ArrayListObjektreferenzvariablen des Objekts, das im Speicher gespeichert ist. Verweist list1also auf dieses Objekt und es ist nichts weiter als eine Variable. Und in der zweiten Codezeile kopieren wir den Verweis list1auf list2. Kommen wir nun zu Ihrer Frage zurück, wenn ich das tue:

list1 = null;

Das bedeutet, dass list1kein Objekt mehr list2referenziert wird, das im Speicher gespeichert ist, und dass auch nichts mehr referenziert werden muss. Wenn Sie also die Größe von überprüfen list2:

list2.size(); //it gives you 0

Hier kommt also das Konzept des Garbage Collector, das besagt: "Sie brauchen sich keine Sorgen um die Freigabe des vom Objekt gehaltenen Speichers zu machen. Ich werde dies tun, wenn ich feststelle, dass es nicht mehr im Programm verwendet wird und JVM mich verwaltet."

Ich hoffe es klärt das Konzept.

Aman Goel
quelle
0

Einer der Gründe dafür ist die Beseitigung veralteter Objektreferenzen. Den Text können Sie hier lesen .

Sudip Bhandari
quelle