System.gc() – gut gemeint, aber meist unnötig

Vor ein paar Tagen durfte ich mal wieder einen Blick in Fremdcode werfen, um zu sehen, wie die entsprechende Implementierung realisiert wurde. Eine an sich recht übersichtliche Methode, endete dann mit einem System.gc();. Die Intention ist schon klar: “Gib bitte all den Speicher frei, der jetzt noch durch herrenlose Objekte belegt wird.” Das ist zwar gut gemeint, aber im Regelfall erstens unnötig und zweitens oft sogar kontraproduktiv.

Zu den Fakten. Als erstes sollte an der Stelle ein Blick in die API von System.gc() folgen:

Calling the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse. When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects.

Das heißt die API impliziert hier schon, dass man sich nicht darauf verlassen kann und soll, dass nach dem Aufruf überhaupt irgendetwas passiert ist. (Die nächst schlimmere Lösung, die ich auch ab und zu gesehen habe, ist dann eine Schleife, in der der GC X-mal aufgerufen wird.)

Auf Stackoverflow findet sich eine interessante Diskussion, ob und warum der GC-Aufruf keine gute Idee ist – bzw WANN es eine gute Idee ist, den Aufruf wirklich zu machen. Ein kleines Fazit der ganzen Diskussion:

  • Es wird oft gesagt, dass es Bad-Practice ist, also lass es (naja okay, kein gutes Argument)
  • Die JVM kennt viele GCs, man weiß zur Ausführungszeit gar nicht, welcher GC aktiviert ist und wie er konfiguriert ist. Einfach mal auf der Java HotSpot VM Options nach “garbage collection” suchen.
  • Die sinnvollere Art den GC zu konfigurieren ist nicht ihn einfach aufzurufen sondern ihn zu konfigurieren.
  • Je nach Implementierung, kann ein Stop-The-World passieren. Also dass das gesamte Programm zur Garbage Collection steht.
  • Eventuell geschieht auch gar nichts: http://bugs.sun.com/view_bug.do?bug_id=6668279
  • Die Speicherverwaltung in der JVM ist nicht nur in Stack und Heap unterteilt. Der Heap ist unterteilt in Heap,Young,Tenured und Perm generation. Sun hat viel Zeit in die intelligente Garbage Collection gesteckt. Wenn man sich nicht mit Speicher Verwaltung und Garbage Collection beschäftigt, macht man es wahrscheinlich weniger intelligent als die Automatik.
  • Oracle/Sun schlägt im Tuning Guide “Tuning the Java Runtime System” explizit vor, den Aufruf auszuschalten.
  • .. und vermutlich noch einige weitere Argumente.

Wann es dann wirkliche eine gute Idee ist, den garbage collect selbst aufzurufen, ist dort auch zu lesen. Unter anderem, ist ein manueller GC sinnvoll wenn:

  • Wenn man ein Speicherleck finden will, kann es sinnvoll sein, zu bestimmten Checkpunkten die Garbage Collection zu forcieren (oder es zu versuchen)
  • Wenn man den Speicherverbrauch von Klassen bestimmen will (siehe Posting).
  • Nach einer umfangreichen und langen Initialisierungsphase ist die Tenured-Generation vermutlich voll mit Objekten, die man nicht mehr brauchen wird und die man gleich aufzuräumen will/muss, um zu verhindern, dass der erste spätere GCs lange braucht, da dort enorm viele Objekte herumlungern die schon lange nicht mehr gebraucht werden.
  • Nach einer kurzen Initialisierungsphase sind eventuell viele – später nicht mehr benötigte – Objekte im Speicher, die gar nicht erst in die Tenured Generation wandern sollen.

Interessante Links zum Thema:

Tuning the Java Runtime System