Memory Leaks in Java – eine Systematische Analyse – Symptome

Beobachtungen

Bei Speicherproblemen handelt es sich häufig um Probleme welche nicht direkt reproduzierbar sind. Eine Fehlersuche wird hierdurch erschwert.
Professionelle Profiling Tools helfen hier nur bedingt weiter. Mit ihnen können wir Metriken aufnehmen, Systematik und Erfahrung ist dennoch notwendig, um die Informationen korrekt und zielgerichtet zu bewerten.
Zur systematischen Analyse empfiehlt sich eine Beobachtung der Symptome, welche bereits wichtige Informationen zum Eingrenzen des Fehlers liefern können.
Es gibt unterschiedliche Kategorien von Beobachtungen

  • Exceptions
  • Systemverhalten
  • Zeitliches Verhalten / (un-)deterministisches Auftreten
  • durch Tooling gelieferte Ergebnisse

Ein Bewerten unterschiedlicher Beobachtungen hilft hierbei den Fehler einzugrenzen und mögliche Ursachen auszuschließen. Durch ein Bewerten aller Symptome im Zusammenspiel können die möglichen Ursachen eingeschränkt werden.

Exceptions

Wie in einem vorherigen Blogposts über OutOfMemoryError’s beschrieben, hilft eine genaue Betrachtung der Fehlermeldung einer solchen Exception, den betroffenen Speicherbereich einzugrenzen. Dies ist der erste Schritt um weitere Analysen einzuleiten.
Wird der Fehler im Perm Gen Bereich lokalisiert, so ist das weitere Vorgehen zur Eingrenzung ein anderes, als bei Fehlern auf dem Heap Speicher. Auch der Einsatz von weiteren Tools und das Bewerten der dort gesammelten Informationen unterscheiden sich je nach Speicherbereich.

Systemverhalten

Ein fehlerhaftes Speichermanagement hat auch Auswirkungen auf das Laufzeitverhalten der Java-Applikation. Häufige und aufwendige Garbage Collector-Läufe zeigen sich in schlechter System Performance und hoher CPU Auslastung. Häufiges Phänomenen ist eine Verschlechterung des Durchsatzes über die Zeit. Nach Neustart eines Servers operiert dieser performant. Das Laufzeitverhalten verschlechtert sich aber mit zunehmender Up-Time.

Zeitliches Verhalten / (un-)deterministisches Auftreten

Eine Betrachtung des Zeitpunktes und der Regelmäßigkeit wann ein Fehler auftritt stellt häufig eine wichtige Information zur Verfügung.
Speicherprobleme sind häufig nicht leicht reproduzierbar. Häufig kann die Beobachtung getroffen werden, dass der Fehlerzeitpunkt dennoch eine Regelmäßigkeit aufweist.
„Der Fehler tritt nur etwa einmal die Woche auf“ ist bereits ein Indiz für ein eventuelles Speicher Leck, welches sich über die Zeit aufbaut.
Wie unter dem Punkt „Systemverhalten“ bereits aufgeführt, operiert ein System mit Speicher Leck nach dem Neustart meist in seinen vorgesehen Toleranzen. Nach längerer Laufzeit verschlechtert sich das Systemverhalten wie der Datendurchsatz.
Undeterministisch auftretende Fehler können zum Beispiel auf Fehler hindeuten, welche zu Zeiten erhöhten Useraufkommen, und somit konkurrierenden Zugriff, auftreten. Dieses Verhalten kann unter anderem bei ungünstiger Verwendung von Temporärem Speicher auftreten.
Fehler in der Nacht oder zu bestimmten Uhrzeiten können darüber hinaus auf einen Job oder Datenimport hindeuten und damit verbundener ungünstiger Behandlung von Massendaten.
Ein Beobachten des zeitlichen Verhaltens ist oft ein wichtiges Indiz für die Fehlersuche.

Durch Tooling gelieferte Ergebnisse

Tools liefern weitere wichtige Erkenntnisse. Die Menge der hier gelieferten Informationen kann die Analyse erschweren. Durch vorangehende Beobachtungen kann sowohl die Toolauswahl erleichtert werden, also auch das Augenmerk auf die wichtigen Informationen gerichtet werden.
Für die meisten gängigen Probleme sind unterschiedliche Tools geeignet. Gerade der Bereich des Speichermanagments wird von vielen Tools abgedeckt.

Hinweise und ihre Ursache

Anbei ein Ausschnitt aus möglichen sichtbaren Memory-Problemen einer Java Anwendung und Hinweise wie diese mit möglichen Ursachen zusammenhängen können. Ein Anspruch auf Vollständigkeit möchte ich hierbei nicht erheben, aber die Komplexität, sowie das Vorhandensein von Abhängigkeiten zeigen.
systematicmemoryanalysis
Als Beispiel greifen wir eine Java-Applikation heraus. Ein unerwartetes Beenden der Java VM deutet auf ein mögliches Speicherproblem hin, welches es zu verifizieren gilt. In dem Log finden wir eine OutOfMemoryError welche uns weitere Informationen zu der Ursache liefert. Die Meldung zeigt, dass die Suche im Bereich des PermGen’s fortgesetzt werden sollte.
Bevor wir zu einem Heraufsetzen des Perm Gen Speichers ansetzen, wollen wir zuerst Fehlerursachen im Code ausschließen.
Hier haben wir, nach der Feststellung das es Probleme im Bereich Perm Gen gibt, den Internal String Buffer, das ClassLoading unseres Applikation Servers, Statische Konstanten oder auch ein Class Loader leak, im Bereich von Bytecode-Manipulation-Libraries wie clib, im Verdacht. Dies gilt es weiter einzuschränken.
Das Tool jmap (mit der Option „-histo“) welches mit der JVM mitgeliefert wird kann uns hier weitere Informationen liefern. Wir suchen nach Klassen welche mehrmalig mit unterschiedlicher Anzahl von Instanzen auftauchen.
Finden wir solche Vorkommen, so werden Klassen mehrfach geladen. Über die Class Referenzen, aus einen heap dump welche wir im Anschluss analysieren, können wir herausfinden, dass das gleiche jar von unterschiedlichen Classloadern geladen wurden ist.
Symptome folgen
Das mehrfache Laden der gleichen Klassen finden wir im JEE oder OSGI Umfeld häufig vor. Unterschiedliche Classloader werden hier verwendet, um einzelne Bundles oder JEE-Module zu separieren.
Gemeinsam verwendete Bibliotheken und deren Klassen werden hierdurch mehrfach geladen.

Zugegeben ein Überlauf des Perm Gen-Memories ist ein einfaches Beispiel, verdeutlicht aber das Vorgehen anhand von Symptomen mögliche Ursachen auszuschließen. Es hilft Profiling Tools erst zielgerichtet und effektiv einzusetzen.

Short URL for this post: http://wp.me/p4nxik-2bg
This entry was posted in Java Basics, Java Runtimes - VM, Appserver & Cloud and tagged , , , . Bookmark the permalink.

Leave a Reply