Memory Leak - Ursache?

mccae

Senfdazugeber
Huhu,

Ich habe folgendes Problem:

Eine von mir auf einem Linux (Debian Lenny mit JRE 1.6.0_19) Server laufende Applikation frisst dich durch meinen RAM.

Was ich damit meine?
Es verschwinden rund 50Mb pro Woche im Nirvana.

Laut htop verbraucht auch meine Applikation diesen Speicher.

Deshalb habe ich Heapdumps angefertigt, und habe festgestellt, dass das Programm laut Heapdump um die 5Mb verbraucht.
Die Java internen Funktionen liefern beim Verbrauch auch Werte zwischen 1 und 7Mb (wobei immer rund 15Mb allocated sind).
Diese Werte sind auch stabil.

Das kann aber irgendwie nicht sein!

Wie kommt es dazu, dass laut meinem Taskmanager eben meine Java App der Speicherfresser ist?

Fakt ist ja, dass nach rund 3 Monaten Dauerbetrieb der ganze Server mit einem OutOfMemory absemmelt.

Trotzdem ist der Heap mikrig, und nach mehreren Analysen (VisualVM, etc.) OK.

Kann mir da jemand helfen?

mfg,
Martin

PS

Ich habe zwei Screenshots vom Taskmanager gemacht.
Sie zeigen den selben Prozess nach einer Zeitspanne von 4 Tagen.
 

Anhänge

  • memleak2.jpg
    memleak2.jpg
    197,8 KB · Aufrufe: 138
  • memleak.jpg
    memleak.jpg
    247,3 KB · Aufrufe: 127
Zuletzt bearbeitet:
Hallo,

hast du die Anwendung schonmal mit der JConsole oder besser mit JVisualVM eine Weile lang beobachtet?

Welche Meldung bekommst du denn beim OutOfMermoryError genau? Verwendet deine Anwendung viel Reflection oder eine Library welche dynamisch Klassen generiert? cglib? Hibernate? etc.

Bekommst du vielleicht eine OutOfMemoryError für die PermGen ? Verwendest du native Bibliotheken?
Arbeitest du viel mit Zip Archiven?

Gruß Tom
 
Hallo,

Zuallererst möchte ich mich für deine Antwort bedanken.

hast du die Anwendung schonmal mit der JConsole oder besser mit JVisualVM eine Weile lang beobachtet?

Ja, das habe ich.
Dabei zeigt sich ein meiner Meinung nach Typisches Bild was den Speicher angeht:

Der Verbrauch geht von 0Mb auf ~ 5Mb und wird, nachdem der GC zugeschlagen hat, wieder niedrig.
Das ganze ist dann relativ stabil.
Kein Anstieg - auch kein langsamer linearer - ist zu entdecken.


Welche Meldung bekommst du denn beim OutOfMermoryError genau?

Das weiß ich nicht, da ich beim ersten Mal als das passiert ist, nicht viel getan habe.
Um einen weiteren OutOfMemoryError zu produzieren müsste ich den ein oder anderen Monat warten, bis der Speicher ausgeht.


Verwendet deine Anwendung viel Reflection oder eine Library welche dynamisch Klassen generiert?

Nein zu beiden Fragen, würde ich sagen.
Ich verwende Reflection nur um ein paar JARs während der Laufzeit einzubinden, indem ich addURL im URLClassLoader auf "accessible" setze.
Das wird's aber nie sein...


Bekommst du vielleicht eine OutOfMemoryError für die PermGen ? Verwendest du native Bibliotheken?
Arbeitest du viel mit Zip Archiven?

Ob ich eine "OutOfMemoryError für die PermGen" bekomme, weiß ich nicht.
Was aber wahr ist, ist dass ein permanenter Speicheranstieg laut JConsle im "Non-Heap" Bereich stattfindet.

Ja, ich eine native Drittanbieter Bilbiothek.
Es werden wohl einige hundert Aufrufe von "native" Methoden in der Stunde seien.

Von der JAVA-Seite aus konnte ich im Code der Bibliothek nichts finden.

ZIP Archive verwende ich auch keine...


Weiters wollte ich noch sagen, dass ich mir mit dem "Memory Analyzer" von IBM ein paar Heapdumps anzeigen ließ.

Laut dem Tool liegt ein mögliches Leak bei der Klasse "sun.nio.cs.ext.GB18030".
Da aber die Instanzen der klasse nur 190kb Speicher verbrauchen, kann's daran wohl auch nicht liegen.

Ich habe diesem Post einen Screenshot des "Dominator Trees" des MemoryAnalyzers angehängt.

Ich weiß wirklich nicht, was ich noch tun kann.

Gruß,
Martin
 

Anhänge

  • dominatortree.jpg
    dominatortree.jpg
    258,1 KB · Aufrufe: 115
Zuletzt bearbeitet:
Der Heap ist hier sicher nicht das Problem. Nach meinen Beobachtungen ist es sogar besser weniger heap bereit zu stellen, weil dann der verfügbare mögliche Non-Heap größer ist für die nativen Bibliotheken.(Windows 32bit hat maximal 2GB für einen Prozess)

Ich vermute stark, dass die nativen Objekte nicht richtig aufgeräumt werden oder das der Speicher in manchen Fällen einfach nicht ausreicht. Daher ein Fehler in der Bilbiothek oder in der Benutzung dieser.
Bekommst du HotSpot-Fehlerdateien? Diese werden automatisch im Arbeitsverzeichnis des Programms abgelegt.

(So ein Problem hatte ich beruflich mit einer nativen Bibliothek auch schon.
Die Lösung bei uns war allerdings, dass wir eine reine Java-Bibliothek bekommen.)
 
Huhu,

Ich vermute stark, dass die nativen Objekte nicht richtig aufgeräumt werden oder das der Speicher in manchen Fällen einfach nicht ausreicht. Daher ein Fehler in der Bilbiothek oder in der Benutzung dieser.

Die native Bibliothek (.dll / .so) wird eingebunden, und es werden bei mir statische "native" Methoden aufgerufen. (Mit Parametern und Rückgabewerten).

Im C Teil des ganzen wird der Aufruf nur direkt zu einer Funktion einer Standardbibliothek weitergeleitet (aus windows.h usw.).

Musste sich der ersteller der Bibliothek beim Schreiben des C Codes gedanken über Speicherfreigabe usw. machen?

Denn was passiert bei der Benutzung von JNI auf der anderen Seite mit den übergebenen Parametern?

Ich habe mich damit noch nie befasst.


Wer Zeit und Lust hat einen Blick auf den C Code zu werfen, bittesehr:
Das ganze ist als Datei angehängt.


Jetzt stellt sich ja nur die Frage, ob die vielen nativen Aufrufe ein JNI Technisches Problem sind, oder etwas nicht mit der Library stimmt.


Bekommst du HotSpot-Fehlerdateien?

Nein, noch nie etwas davon gehört...
 

Anhänge

Hallo,

btw. du kannst dir auch automatisch einen Heap Dump bei einem OOME generieren lassen:
-XX:+HeapDumpOnOutOfMemoryError

Gruß Tom
 
Huhu,

Auch ein Heapdump bei einem OOME hilft wenig, da das Problem anscheinend nicht vom Heap kommt...

Als ich heute wieder einmal über SSH den screen sah, kam mir etwas entgegen, was nach einem Threaddump oder so aussieht:

Code:
.Thread.sleep(Native Method)
	at at.co.lipski.twcc2.util.BackupWorker.run(BackupWorker.java:31)
	at java.lang.Thread.run(Unknown Source) "ServerListenerThread" prio=10 tid=0x08301c00 nid=0x438f runnable [0x9fddb000]
	java.lang.Thread.State: RUNNABLE
	at java.net.PlainSocketImpl.socketAccept(Native Method)
	at java.net.PlainSocketImpl.accept(Unknown Source) - locked <0xa598ef28> (a java.net.SocksSocketImpl)
	at java.net.ServerSocket.implAccept(Unknown Source)
	at java.net.ServerSocket.accept(Unknown Source)
	at at.co.lipski.twcc2.net.daemon.Daemon.setUpNetwork(Daemon.java:61)
	at at.co.lipski.twcc2.net.daemon.Daemon.run(Daemon.java:34)
	at java.lang.Thread.run(Unknown Source)
"ConsoleCharReader" prio=10 tid=0x08102000 nid=0x438e runnable [0x9fe6f000]
	java.lang.Thread.State: RUNNABLE
	at jcurses.system.Toolkit.readByte(Native Method) - locked <0xb02efce0> (a java.lang.Class for jcurses.system.Toolkit)
	at jcurses.system.Toolkit.readCharacter(Toolkit.java:664) - locked <0xb02efce0> (a java.lang.Class for jcurses.system.Toolkit)
	at jcurses.widgets.WindowManagerInputThread.run(WindowManager.java:447)
"Low Memory Detector" daemon prio=10 tid=0x080bcc00 nid=0x438c runnable [0x00000000]
	java.lang.Thread.State: RUNNABLE
"CompilerThread1" daemon prio=10 tid=0x080bb400 nid=0x438b waiting on condition [0x00000000]
	java.lang.Thread.State: RUNNABLE
"CompilerThread0" daemon prio=10 tid=0x080b8400 nid=0x438a waiting on condition [0x00000000]
	java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" daemon prio=10 tid=0x080b6c00 nid=0x4389 waiting on condition [0x00000000]
	java.lang.Thread.State: RUNNABLE
"Finalizer" daemon prio=10 tid=0x080a4400 nid=0x4388 in Object.wait() [0xa011f000]
	java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)


Leider ist das alles, was noch im Consolebuffer zu finden war...

Kann ich damit etwas anfangen?
 
Hallo,

ich würde - wie Thomas empfielt - die Option HeapDumpOnOutOfMemoryError setzen und evtl. noch mit HeapDumpPath definieren, wohin das File abgelegt werden soll. Siehe dazu auch Snorcles Doku

Was ich außerdem versuchen würde - vorausgesetzt ihr habt ein Entwicklungs-/Test-/Wie-auch-immer-genannt System - mach einen Lasttest, um den Fehler zu provozieren. Dann musst Du nicht immer Wochenlang warten, um das Problem zu analysieren.

Was mir bei ähnlichen Probleme sehr geholfen hat ist Netbeans. Der Netbeans Profiler ist bisher das Beste was mir unter die Hände gekommen ist. Das zugehörige How-To-Discover-Memory-Leaks ist schon recht gut, aber es gibt noch mehr dazu zu lesen.

Bei Snorcle findet sich in der Dokumentation für jConsole ein Abschnitt über die Speicherbereiche.

Heap and Non-Heap Memory

The Java VM manages two kinds of memory: heap and non-heap memory, both of which are created when the Java VM starts.

Heap memory is the runtime data area from which the Java VM allocates memory for all class instances and arrays. The heap may be of a fixed or variable size. The garbage collector is an automatic memory management system that reclaims heap memory for objects.

Non-heap memory includes a method area shared among all threads and memory required for the internal processing or optimization for the Java VM. It stores per-class structures such as a runtime constant pool, field and method data, and the code for methods and constructors. The method area is logically part of the heap but, depending on the implementation, a Java VM may not garbage collect or compact it. Like the heap memory, the method area may be of a fixed or variable size. The memory for the method area does not need to be contiguous.

In addition to the method area, a Java VM may require memory for internal processing or optimization which also belongs to non-heap memory. For example, the Just-In-Time (JIT) compiler requires memory for storing the native machine code translated from the Java VM code for high performance.
Was interessant wäre ist der genaue Stack deiner OOME. Wenn die nicht abhebt, weil dein Heap voll ist, sondern der andere Bereich, dann liegt Anime-Otaku richtig.
 
Zuletzt bearbeitet:
Zurück