Memory Leaks in Java – eine Systematische Analyse – die Jagd beginnt mit dem OutOfMemoryError

Auf der Suche nach Performanceproblemen und Instabilitäten in Java-basierten-Systemen stößt man immer wieder auf Probleme mit der Speicherverwaltung. Das auch in Java Memory-Leaks auftreten können, sollte jedem Entwickler bewußt sein.

OutOfMemoryError Exception – das deutlicheste Symptom

Das deutlichste Symptom, dass es Probleme in der Speicherverwaltung einer Anwendung gibt, ist sicherlich ein OutOfMemoryError. Da diese Exception mit dem Beenden der JVM daherkommt, ist dies sicherlich auch die schmerzhafteste Variante, da dies naturgemäß häufig einen Produktionsausfall hervorruft und somit direkt auch in der Managment-Ebene aufschlägt.

Ein regelmäßiges Neustarten des Servers zu Mitternacht ist hier meist die schnellste Symptombehebung, behebt aber leider nicht die Einflüsse, die das fehlerhafte Speichermanagment auf Aspekte wie zum Beispiel System-Performance hat. Ein Vergrößern des Heap-Speichers verschiebt die Problematik leider nur, da auch hier die Performance und der Durchsatz der Anwendungen leiden kann, bedingt durch den erhöhten Verwaltungsaufwand des großen Heaps.
Ein neues Problem manifestiert sich in Form von hohen Pause-Zeiten des Garbage Collectors und eine überdurchschnittliche CPU Belastung durch den GC.

Nicht immer handelt es sich um ein Speicherleak bedingt durch nicht freigegebenen Objektreferenzen. Wenn es zu einem OutOfMemoryError kommt, genügt das heraufsetzen des Java Heap Speichers auch nicht immer, um Speicherprobleme in den Griff zu bekommen. Teilweise kann dies, wie erwähnt, sogar negative Auswirkungen auf die Gesamtperformance des Systemes haben.
Wir wollen in diesem Blog Post und in einer Reihe weiterer Blog Postings das Thema Memory Leaks in Java aufgreifen und versuchen, hier eine Systematik herauszustellen um MemoryLeaks zu erkennen und Symptome richtig zu deuten.
Mit dem notwendigen Hintergrundwissen, einem methodischen Vorgehen und natürlich den richtigen Werkzeugen ist das Auffinden und Beheben der meisten Speicherprobleme schnell erledigt.

OutOfMemoryError ist nicht gleich OutOfMemoryError

Schauen wir in die Java-API Dokumentation, so erfahren wir folgendes als Ursache für einen OutOfMemoryError:

Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector.

Ok, die Java Virtual Machine benötigt also mehr Speicher, aber kann diesen nicht allokieren. Leider existiert hier keine weitere Aussage darüber, was denn “Allokation” hier bedeutet.
Schauen wir uns mal das Java Memory Model an, so werden wir feststellen, dass verschiedene Bereiche existieren, in denen Speicher allokiert werden muss. Alle Bereiche können die Ursache für einen OutOfMemoryError sein. Wenn wir hier eine Unterscheidung treffen können, in welchem logischen Bereich die Allokation fehlschlug, können wir bereits die Fehlerursache einschränken.
Auch unterscheiden sich die Maßnahmen, welche wir zur Fehlerbehebungen anwenden sollten.

Wir teilen die Bereiche ein in:

  • heap space
  • methode area (Perm-Space)
  • native area

Die Message des OutOfMemoryErrors liefert uns die Information, welcher Speicherbereich die Ursache des Problems darstellt. Die unterschiedlichen Bereiche erfordern hierauf ein unterschiedliches Vorgehen bei der Fehlersuche und Fehlerbehebung. Bei allen Angaben beziehen wir uns auf die Oracle JVM.

java.lang.OutOfMemoryError: Java heap space
java.lang.OutOfMemoryError: Requested array size exceeds VM limit

An den Messages “Java heap space” und “Requested array size exceeds VM limit” erkennen wir, dass der Java Heap Space die Ursache für den Fehler darstellt. d.h. die in der JVM eingestellte maximale Größe des Heap Speichers wurde erreicht und kann durch die Limitierung nicht erweitert werden.

Da der Heap-Space für Objekte verwendet wird, kann die Ursache zum Beispiel Objektreferenzen sein, welche nicht freigegeben werden, oder allgemein ein zu klein eingestellter Heap.

java.lang.OutOfMemoryError: PermGen space

Wir erkennen, dass der Fehler im permanenten, konstanten Speicherbereich aufgetreten ist. Dieser Speicherbereich wird zum Beispiel verwendet, um Klassen Informationen abzulegen oder auch für den internen String Pool.
Tritt dieser Fehler auf, so können die Ursachen also im Bereich Classloader (z.B. häufiges Redeployment, bei dem Klassen in mehreren Versionen vorliegen) oder im Bereich des String-Pools gesucht werden.
Der PermGen muss per Spezifikation nicht durch einen Garbage Collector aufgeräumt werden.

java.lang.OutOfMemoryError: unable to create new native thread

Der Fehler tritt beim Anlegen eines Threads innerhalb der JVM auf. Für jeden Thread wird ein eigener Speicherbereich angelegt, auf welchen dieser exklusiv Zugriff hat. Dieser Bereich ist unabhängig vom Heap oder PermGen Bereich.

Der Fehler tritt zum Beispiel auf, wenn das Speicherlimit für den Java Prozess auf Betriebssystem Ebene erreicht ist. Aber auch beim Erreichen der maximalen Anzahl File Handles für den Benutzer, welcher den Java Prozess ausführt. Es sollte auch das Thread Handling der Applikation überprüft werden, wieviele Threads aktiv sind.

java.lang.OutOfMemoryError: (Native method)

Diese Meldung zeigt an, dass ein Speicher Allokationsfehler auf dem Native Stack aufgetreten ist. Der Native Stack wird beim Aufruf von Methoden über JNI verwendet.
Die Fehlerursache dieses OOM sollte also im Bereich von JNI Methoden gesucht werden.

java.lang.OutOfMemoryError: request bytes for . Out of swap space?

Dieser Fehler tritt auf, wenn das Betriebsystem nicht ausreichend Speicher zum Erweitern des Heap-Memories zur Verfügung stellen kann. Der maximal zur Verfügung stehende Heap Speicher wird beim Start der JVM nicht komplett allokiert, sondern schrittweise bei Bedarf. Bei JVM-Start wird lediglich der als Min-Heap definierte Speicherbereich angefordert. Steigern sich die Anforderungen der Applikation zur Laufzeit, so wird weiterer Speicher vom Betriebssystem angefordert. Kann dieser nicht zur Verfügung gestellt werden, so wird der entsprechende Error geworfen.
Beim Auftreten dieses Fehlers sollte die Analyse auf Betriebssystemebene begonnen werden.

systematic

Fazit

Dieser Blog liefert das erste Rüstzeug zum systematischen Auffinden von Speicherfehlern in Java. Das Verständnis, welcher Speicherbereich betroffen ist, stellt eine erste wichtige Analysestufe dar. Anhand dieses Wissens kann entschieden werden, welche Maßnahmen getroffen werden sollten, um mit einer weitere Analyse zu beginnen.

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

One Response to Memory Leaks in Java – eine Systematische Analyse – die Jagd beginnt mit dem OutOfMemoryError

  1. Pingback: Memory Leaks in Java – eine Systematische Analyse – Symptome - techscouting through the java news

Leave a Reply