System.gc() Aufrufe können schwere Folgen haben

2 Kommentare

Es passiert jedem Entwickler schon mal, daß man Hinweise aus der JavaDoc nicht besonders Ernst nimmt, oder gar überliest. Insbesondere wenn die angesprochenen Probleme lokal nicht (oder scheinbar nicht) auftreten.

Zum Beispiel aus dem java.text.SimpleDateFormat:

 * Date formats are not synchronized.
 * It is recommended to create separate format instances for each thread.
 * If multiple threads access a format concurrently, it must be synchronized
 * externally.

Vielleicht ist die Warnung auch nicht deutlich genug, dadurch daß dort steht: „It is recommended“.
Deshalb sieht man leider häufig folgende Zuweisung welche zu Nebenläufigkeitsproblemen führen kann

private static final SimpleDataFormat DATE_FORMAT = new SimpleDateFormat("yyyy.MM.dd");

Schlimmer ist es hingegen bei System.gc().

Dort findet sich keine Warnung im JavaDoc:

* 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.

Klingt sogar garnicht so schlecht. Jedoch sollte „suggests“ und „best effort“ einen stutzig machen. Falls man auf die Idee kommt System.gc() zu verwenden prüft man vielleicht was im großen weiten Internet dazu gesagt wird, in der Hoffnung auf Erklärungen zu stoßen was tatsächlich passiert. So finden sich auf Stackoverflow gute Ausführungen. Was es genau tut weiß aber keiner so genau, weshalb diese Anweisung so riskant ist.

Ich finde aber, daß ein Bild mehr sagt als viele Worte:

Wir sehen ein relativ großzügig dimensioniertes System eines unserer Kunden mit 14 GB Heap. Diesem Kunden haben wir geholfen den Speicherverbrauch seiner Anwendung zu reduzieren, jedoch gibt es immer noch Pausenzeiten. Der oben abgebildete Graph stammt aus dem Garbage Collection Log und zeigt die Ursache für die Pausen. Die blaue Kurve unten zeigt Garbage Collection. Doch warum gibt es 13 Garbage Collection Spitzen mit bis zu 35 Sekunden, während die zahlreichen anderen Garbage Collections sehr sehr kurz sind?
Die Antwort liegt in der Ursache für diese 13 Garbage Collections. Sie wurde alle durch ein System.gc() aufgerufen.

Der Aufruf von System.gc() unterbindet damit effektiv die Optimierungsmöglichkeiten der Garbage Collection und schlägt sich auf diesem System in relativ langen Pausenzeiten nieder.

Anmerkung: Dieses System lief auf einer Sun 1.6.0.18 64 bit HotSpot Server VM. Als GC sind -XX:+UseConcMarkSweepGC und -XX:+UseParNewGC konfiguriert. System.gc() liefert keine deterministischen Ergebnisse und muss sich nicht zwangsläufig wie hier beschrieben verhalten. Allerdings beobachten auch andere, daß bei CMS und System.gc() auf jeden Fall ein Full GC durchgeführt wird.

Sollte man im eigenen Code keine System.gc() Aufrufe finden, so verstecken sich diese in genutzten Bibliotheken. Unterbinden kann man sie aber über das Server Argument „-XX:+DisableExplicitGC“. Dieses Argument wird von SUN/Oracle empfohlen.

Fabian Lange ist Lead Agent Engineer bei Instana und bei der codecentric als Performance Geek bekannt. Er baut leidenschaftlich gerne schnelle Software und hilft anderen dabei, das Gleiche zu tun.
Er kennt die Java Virtual Machine bis in die letzte Ecke und beherrscht verschiedenste Tools, die JVM, den JIT oder den GC zu verstehen.
Er ist beliebter Vortragender auf zahlreichen Konferenzen und wurde unter anderem mit dem JavaOne Rockstar Award ausgezeichnet.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentare

  • Fabian Lange

    Hi Amrr,
    this is an interesting problem. It is strange that Tenured is not collected at all for you unless you force it. That it never hits OOME is an indication that the memory can be freed (this is why your force also works).

    So the answer for your problem is finding out why no gc is ran.

    Clearing up ThreadLocals is a good idea, but that only helps if you are actually using all the memory. But because your scheduled full gc can free memory you are not using it.

    There is unfortunately no easy answer but I can look at it if you send me some more details via mail: fabian.lange at our codecentric.de domain

  • Fabian Lange

    Dear Friend,

    static means that the same instance of SimpleDateFormat is used by each and every request, thread, user whatever. The fact that people are unaware or neglect is that when this single instance is used at the same time by more than one piece of code it will break. It can hang your jvm if you do. So better dont 🙂

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.