Wann macht System.gc () etwas?

118

Ich weiß, dass die Speicherbereinigung in Java automatisiert ist. Aber ich habe verstanden, dass, wenn Sie System.gc()Ihren Code aufrufen , die JVM möglicherweise entscheidet, an diesem Punkt eine Speicherbereinigung durchzuführen oder nicht. Wie funktioniert das genau? Auf welcher Basis / auf welchen Parametern entscheidet sich die JVM genau, einen GC durchzuführen (oder nicht), wenn sie sieht System.gc()?

Gibt es Beispiele, in welchem ​​Fall es eine gute Idee ist, dies in Ihren Code aufzunehmen?

Michael
quelle
4
Ein zu komplexes Konzept, um es hier im Detail zu beschreiben, aber dieser Artikel sollte Ihnen helfen: chaoticjava.com/posts/how-does-garbage-collection-work
GEOCHET
Das genaue Verhalten hängt von der JVM ab, dh von der Implementierung abhängig.
Thorbjørn Ravn Andersen

Antworten:

59

In der Praxis wird normalerweise eine Speicherbereinigung durchgeführt. Die Antwort hängt von vielen Faktoren ab, z. B. von der JVM, auf der Sie ausgeführt werden, in welchem ​​Modus und in welchem ​​Garbage Collection-Algorithmus.

Ich würde mich in Ihrem Code nicht darauf verlassen. Wenn die JVM im Begriff ist, einen OutOfMemoryError auszulösen, wird der Aufruf von System.gc () ihn nicht stoppen, da der Garbage Collector versucht, so viel wie möglich freizugeben, bevor es zu diesem Extrem kommt. Das einzige Mal, dass ich es in der Praxis gesehen habe, ist in IDEs, wo es an eine Schaltfläche angehängt ist, auf die ein Benutzer klicken kann, aber selbst dort ist es nicht besonders nützlich.

Jodonnell
quelle
3
Sie können eine Speicherbereinigung in jconsole erzwingen
Benedikt Waldvogel
25
@bene Kannst du es wirklich "erzwingen"? Ruft jconsole nur System.gc () auf?
John Meagher
4
Ich würde System.gc()in einem Ladebildschirm für ein Videospiel verwenden. Zu diesem Zeitpunkt wäre es mir eigentlich egal, ob der Anruf alles löschte, was er konnte, oder überhaupt nichts tat. Ich würde es jedoch vorziehen, dass es "Aufwand für das Recycling nicht verwendeter Objekte" während des Ladebildschirms und nicht während des Spiels erfordert.
Kröw
30

Das einzige Beispiel, an das ich denken kann, wo es sinnvoll ist, System.gc () aufzurufen, ist die Profilerstellung einer Anwendung, um nach möglichen Speicherlecks zu suchen. Ich glaube, die Profiler rufen diese Methode auf, bevor sie einen Speicherschnappschuss machen.

Guillermo Vasconcelos
quelle
4
Ja, das mache ich auch. Die von Runtime.freeMemory () zurückgegebenen Werte sind beispielsweise erst nach einem System.gc () wirklich aussagekräftig, da Sie normalerweise wissen möchten, wie viel Speicher durch nicht sammelbare Instanzen blockiert wird. Natürlich nur zum Debuggen / Speichern von Profilen, niemals in der Produktion.
Sleske
Nein, es ist auch gut für viele andere Szenarien. Angenommen, Sie haben ein einzelnes Muster, das den Lebenszyklus berücksichtigt. Wenn Sie in onStop () die Instanz auf Null setzen, ist es eine gute Idee, System.gc () aufzurufen, um dies zu unterstützen.
Portfoliobuilder
26

Die Java-Sprachspezifikation garantiert nicht, dass die JVM beim Aufruf einen GC startet System.gc(). Dies ist der Grund dafür, dass "zu diesem Zeitpunkt möglicherweise eine GC durchgeführt wird oder nicht".

Wenn Sie sich nun den OpenJDK-Quellcode ansehen , der das Rückgrat von Oracle JVM darstellt, werden Sie feststellen, dass ein Aufruf von System.gc()einen GC-Zyklus startet. Wenn Sie eine andere JVM wie J9 verwenden, müssen Sie deren Dokumentation überprüfen, um die Antwort zu finden. Zum Beispiel verfügt Azul's JVM über einen Garbage Collector, der kontinuierlich ausgeführt wird, sodass ein Aufruf von System.gc()nichts zu tun hat

Einige andere Antworten erwähnen das Starten eines GC in JConsole oder VisualVM. Grundsätzlich führen diese Tools einen Fernanruf an System.gc().

Normalerweise möchten Sie keinen Garbage Collection-Zyklus mit Ihrem Code starten, da dies die Semantik Ihrer Anwendung beeinträchtigt. Ihre Anwendung erledigt einige geschäftliche Aufgaben, die JVM kümmert sich um die Speicherverwaltung. Sie sollten diese Bedenken getrennt halten (lassen Sie Ihre Anwendung keine Speicherverwaltung durchführen, sondern konzentrieren Sie sich auf das Geschäft).

Es gibt jedoch nur wenige Fälle, in denen ein Anruf bei System.gc()verständlich sein könnte. Betrachten Sie beispielsweise Mikrobenchmarks. Niemand möchte, dass ein GC-Zyklus mitten in einem Mikrobenchmark stattfindet. Sie können also zwischen jeder Messung einen GC-Zyklus auslösen, um sicherzustellen, dass jede Messung mit einem leeren Heap beginnt.

Pierre Laporte
quelle
26

Sie haben keine Kontrolle über GC in Java - die VM entscheidet. Ich bin noch nie auf einen Fall gestoßen, in dem System.gc()es nötig ist . Da ein System.gc()Aufruf lediglich EMPFIEHLT, dass die VM eine Garbage Collection und auch eine FULL Garbage Collection (alte und neue Generationen in einem Heap mit mehreren Generationen) durchführt, kann dies dazu führen, dass MEHR CPU-Zyklen verbraucht werden als erforderlich.

In einigen Fällen kann es sinnvoll sein, der VM vorzuschlagen, JETZT eine vollständige Erfassung durchzuführen, da Sie möglicherweise wissen, dass die Anwendung die nächsten Minuten im Leerlauf bleibt, bevor schweres Heben auftritt. Zum Beispiel direkt nach der Initialisierung vieler temporärer Objekte während des Starts der Anwendung (dh ich habe gerade eine TONNE Informationen zwischengespeichert und weiß, dass ich für eine Minute oder so nicht viel Aktivität bekomme). Denken Sie an eine IDE wie das Starten von Eclipse - die Initialisierung ist sehr umfangreich. Daher ist es möglicherweise unmittelbar nach der Initialisierung sinnvoll, an diesem Punkt eine vollständige GC durchzuführen.

DustinB
quelle
17

Sie müssen sehr vorsichtig sein, wenn Sie anrufen System.gc(). Durch das Aufrufen kann Ihre Anwendung unnötige Leistungsprobleme verursachen, und es wird nicht garantiert, dass tatsächlich eine Sammlung ausgeführt wird. Es ist tatsächlich möglich, explizit System.gc()über das Java-Argument zu deaktivieren -XX:+DisableExplicitGC.

Ich würde dringend empfehlen, die bei Java HotSpot Garbage Collection verfügbaren Dokumente zu lesen, um detailliertere Informationen zur Garbage Collection zu erhalten.

David Schlosnagle
quelle
Meine Android-Anwendung löst immer eine OutOfMemoryException aus. Daher habe ich darüber nachgedacht, System.gc () in der onDestroy-Funktion meiner Klasse zu verwenden, um alle unerwünschten Referenzen und Objekte zu löschen. Ist es ein guter Ansatz
Sagar Devanga
2
@Sagar Devanga Es ist unwahrscheinlich, dass es hilfreich ist, den GC wie beschrieben manuell anzurufen. Vor dem Werfen eines OOM hat die JVM bereits versucht, Müll zu sammeln, um Speicher
freizugeben
10

System.gc()wird von der VM implementiert und ist implementierungsspezifisch. Der Implementierer könnte zum Beispiel einfach zurückkehren und nichts tun.

Wenn Sie eine manuelle Sammlung erstellen möchten, können Sie dies möglicherweise nur tun, wenn Sie eine große Sammlung mit vielen kleineren Sammlungen verlassen - Map<String,<LinkedList>> beispielsweise eine - und dann und möchten versuchen, den Perf-Hit zu erzielen dort, aber zum größten Teil sollten Sie sich darüber keine Sorgen machen. Der GC weiß es leider die meiste Zeit besser als Sie.

Patrick
quelle
8

Wenn Sie direkte Speicherpuffer verwenden, führt die JVM den GC nicht für Sie aus, selbst wenn Ihnen der direkte Speicher ausgeht.

Wenn Sie aufrufen ByteBuffer.allocateDirect()und einen OutOfMemoryError erhalten, können Sie feststellen, dass dieser Aufruf in Ordnung ist, nachdem Sie einen GC manuell ausgelöst haben.

Peter Lawrey
quelle
Wollen Sie damit sagen, dass System.gc () direkte Zuordnungen sammelt, während dies bei der JVM normalerweise nicht der Fall ist (bevor ein OOM ausgelöst wird)? Weil ich denke, dass das falsch ist. Der einzige Grund, warum eine Zuweisung nach einem expliziten GC erfolgreich sein könnte, ist die zusätzliche Zeit und die Wahrscheinlichkeit, dass ein In-Heap-Objekt mit einem Finalizer freigegeben wird (und ein direkt zugewiesenes Objekt freigegeben wird).
eckes
In der neuesten Version von Java 6 führt der Code in allocateBuffer immer ein System.gc () aus, bevor ein OutOfMemoryError ausgelöst wird. Im Finalisierungscode gibt es eine Verknüpfung, für Cleanersdie der direkte Speicher verwendet wird. dh es wird vom Finalisator-Thread nicht freigegeben, da dies zu langsam sein kann. Trotzdem ist es möglich, ein OOME zu erhalten, wenn Sie besonders schnell direkte Speicherbereiche erstellen. Die Lösung besteht darin, dies nicht zu tun und Ihre direkten Speicherbereiche wiederzuverwenden, wo Sie können.
Peter Lawrey
1
Danke, das klingt eher nach meiner Erfahrung. Ich denke, die Antwort ist nur für ältere Java-Versionen richtig.
eckes
5

Die meisten JVMs starten einen GC (abhängig vom Schalter -XX: DiableExplicitGC und -XX: + ExplicitGCInvokesConcurrent). Die Spezifikation ist jedoch weniger genau definiert, um später bessere Implementierungen zu ermöglichen.

Die Spezifikation muss geklärt werden: Fehler # 6668279: (spec) System.gc () sollte anzeigen, dass wir die Verwendung nicht empfehlen und das Verhalten nicht garantieren

Intern wird die gc-Methode von RMI und NIO verwendet und erfordert eine synchrone Ausführung. Dies wird derzeit diskutiert:

Fehler # 5025281: Erlaube System.gc (), gleichzeitige (nicht die Welt stoppende) vollständige Sammlungen auszulösen

eckes
quelle
3

Garbage Collectionist gut in Java, wenn wir in Java codierte Software in Desktop / Laptop / Server ausführen. Sie können anrufen System.gc()oder Runtime.getRuntime().gc()in Java.

Beachten Sie nur, dass keiner dieser Anrufe garantiert etwas bewirkt. Sie sind nur ein Vorschlag für das JVM, den Garbage Collector auszuführen. Es liegt an der JVM, ob sie den GC ausführt oder nicht. Also, kurze Antwort: Wir wissen nicht, wann es läuft. Längere Antwort: JVM würde gc ausführen, wenn es Zeit dafür hat.

Ich glaube, das gilt auch für Android. Dies kann jedoch Ihr System verlangsamen.

Naveen
quelle
JVM würde gc ausführen, wenn es Zeit dafür hat : Nicht immer wahr, Jvm kann gc ausführen, wenn der Eden-Speicher voll ist.
Abdelmouheimen
2

Normalerweise führt die VM vor dem Auslösen einer OutOfMemoryException automatisch eine Garbage Collection durch. Das Hinzufügen eines expliziten Aufrufs sollte daher nicht helfen, es sei denn, dies verschiebt den Leistungseinbruch möglicherweise auf einen früheren Zeitpunkt.

Ich glaube jedoch, dass ich auf einen Fall gestoßen bin, in dem dies relevant sein könnte. Ich bin mir jedoch nicht sicher, da ich noch nicht getestet habe, ob es irgendwelche Auswirkungen hat:

Wenn Sie eine Datei im Speicher abbilden, löst der Aufruf von map () meiner Meinung nach eine IOException aus, wenn kein ausreichend großer Speicherblock verfügbar ist. Eine Garbage Collection kurz vor der map () - Datei könnte helfen, dies zu verhindern, denke ich. Was denken Sie?


quelle
2

Wir können niemals die Müllabfuhr erzwingen. System.gc schlägt vm nur für die Speicherbereinigung vor. Allerdings weiß niemand wirklich, wann der Mechanismus ausgeführt wird. Dies entspricht den JSR-Spezifikationen.

lwpro2
quelle
2

Es gibt viel zu sagen, wenn man sich die Zeit nimmt, um die verschiedenen Einstellungen für die Speicherbereinigung zu testen, aber wie oben erwähnt, ist dies normalerweise nicht sinnvoll.

Ich arbeite derzeit an einem Projekt mit einer speicherbeschränkten Umgebung und relativ großen Datenmengen. Es gibt einige große Datenmengen, die meine Umgebung an ihre Grenzen bringen, und obwohl ich die Speichernutzung so reduzieren konnte dass es theoretisch gut funktionieren sollte, ich würde immer noch Heap-Space-Fehler bekommen - die ausführlichen GC-Optionen zeigten mir, dass es versuchte, Müll zu sammeln, aber ohne Erfolg. Im Debugger konnte ich System.gc () ausführen und sicher war "viel" Speicher verfügbar ... nicht viel Extra, aber genug.

Folglich ruft meine Anwendung System.gc () nur dann auf, wenn sie in das Codesegment eingibt, in dem große Puffer zugewiesen werden, die für die Verarbeitung der Daten erforderlich sind, und ein Test des verfügbaren freien Speichers zeigt an, dass dies nicht der Fall ist garantiert, um es zu haben. Insbesondere betrachte ich eine 1-GB-Umgebung, in der mindestens 300 MB mit statischen Daten belegt sind, wobei der Großteil der nicht statischen Daten ausführungsbezogen ist, es sei denn, die verarbeiteten Daten sind mindestens 100 bis 200 MB groß die Quelle. Dies alles ist Teil eines automatischen Datenkonvertierungsprozesses, sodass die Daten auf lange Sicht nur für relativ kurze Zeiträume vorhanden sind.

Obwohl Informationen über die verschiedenen Optionen zum Optimieren des Garbage Collectors verfügbar sind, scheint dies leider weitgehend ein experimenteller Prozess zu sein, und die Einzelheiten der unteren Ebene, die zum Verständnis des Umgangs mit diesen spezifischen Situationen erforderlich sind, sind nicht leicht zu erhalten.

Obwohl ich System.gc () verwende, habe ich trotzdem die Verwendung von Befehlszeilenparametern fortgesetzt und es geschafft, die Gesamtverarbeitungszeit meiner Anwendung um einen relativ erheblichen Betrag zu verbessern, obwohl ich nicht in der Lage war, darüber hinwegzukommen Stolperstein durch die Arbeit mit den größeren Datenblöcken. Davon abgesehen ist System.gc () ein Tool ... ein sehr unzuverlässiges Tool, und wenn Sie nicht vorsichtig sind, wie Sie es verwenden, werden Sie sich wünschen, dass es nicht öfter funktioniert als nicht.

Kyune
quelle
2

Wenn Sie wissen möchten, ob Ihr System.gc()aufgerufen wird, können Sie mit dem neuen Java 7-Update 4 eine Benachrichtigung erhalten, wenn die JVM die Garbage Collection durchführt.

Ich bin nicht 100% sicher , dass die GarbageCollectorMXBean Klasse war einleitet in Java 7 Update 4 aber, weil ich es nicht in den Release Notes finden konnte, aber ich fand die Informationen in der javaperformancetuning .com - Website

Shervin Asgari
quelle
0

Ich kann mir kein konkretes Beispiel vorstellen, wenn es gut ist, explizite GC auszuführen.

Im Allgemeinen kann das Ausführen eines expliziten GC mehr Schaden als Nutzen verursachen, da ein expliziter GC eine vollständige Sammlung auslöst, die erheblich länger dauert, wenn jedes Objekt durchlaufen wird. Wenn dieser explizite gc wiederholt aufgerufen wird, kann dies leicht zu einer langsamen Anwendung führen, da viel Zeit für die Ausführung vollständiger GCs aufgewendet wird.

Wenn Sie alternativ mit einem Heap-Analysator über den Heap gehen und vermuten, dass eine Bibliothekskomponente explizite GCs aufruft, können Sie diese deaktivieren, indem Sie den JVM-Parametern Folgendes hinzufügen: gc = -XX: + DisableExplicitGC.

zxcv
quelle
2
Wir rufen System.gc () auf, um zu versuchen, unsere MappedByteBuffer-Objekte zu schließen, die ansonsten nicht geschlossen sind und daher nicht für andere Prozesse oder zum Abschneiden von Dateien verfügbar sind, obwohl wir für die Objekte 'close ()' aufrufen. Leider schließt es sie immer noch nicht immer.
Tim Cooper
0

Accroding zu Denken in Java von Bruce Eckel, ein Anwendungsfall für expliziten System.gc () Aufruf ist , wenn Sie Kraft Abschluss mögen, dh den Aufruf Finalisierung Methode.

Sternchen
quelle
NB: Es gibt eine eigene Methode, um den Finalisierungslauf zu erzwingen. (Das Problem besteht eigentlich nicht darin, Finalizer auszuführen, sondern zu erkennen, dass ein Objekt finalisiert werden kann, weil es nicht referenziert ist)
eckes
-1

Während system.gc funktioniert, wird die Welt gestoppt: Alle Antworten werden gestoppt, sodass der Garbage Collector jedes Objekt scannen kann, um zu überprüfen, ob es gelöscht werden muss. Wenn es sich bei der Anwendung um ein Webprojekt handelt, werden alle Anforderungen gestoppt, bis gc abgeschlossen ist. Dies führt dazu, dass Ihr Webprojekt nicht in einem Monent arbeiten kann.

Fleture
quelle