MemoryLeak nicht zu finden

OnDemand

Top Contributor
Moin, hab ein MemoryLeak in meiner Spring Boot App, irgendwo in einer Klasse, finde es aber nicht.

Habe mir remote mit jmap -dump:format=b,file=/opt/tmp/heapdump.bin 37320 einen dump erstellt und in VisualVM geöffnet. Ich sehe da, dass ein byte array ausser Rand und Band läuft und das wohl die Stelle ist welche es zu erforschen gilt.

Nur habe ich keinen blassen Schimmer wo ich anfangen soll, da mit keinerlei Klasse oder so in Visual ausgegeben wird:

1660367454704.png
und als references steht nix besonders

1660367486008.png

Ich habe die Vermutung, dass eine Klasse immer und immer wieder benutzt wird, obwohl sie jedes Mal neu erstellt werden soll.
Ich habe einen Quartz Job welches jede 15 Minuten aufgerufen wird.

Der Quartzjob ist mit @Service annotiert, Der Quartzjob ist mit @DisallowConcurrentExecution annotiert, nicht aber mit @Service kann es sein, dass jedesmal das selbe Jobobject benutzt wird und darin eine Liste irgendwo stetig wächst? Eigentlich mache ich auf alle Maps und Lists .clear() wenn ich sie nicht mehr brauche, eben um RAM zu sparen. Aber irgendwie klappt das nicht.

Hat jemand ne Idee wie ich dem Leak auf die Spur kommen kann?
 
Solange du nicht wirklich einen OutOfMemoryError bekommst, würde ich mir keine Sorgen machen.
Die kombinierte Anzahl an Speicher für diese byte Arrays liegt ja gerade mal bei nur 0,6 Prozent mit seinen ~23 MB.
Das heißt, dein gesamter Prozess verwendet ~4GB Speicher.
Wenn die JVM so viel zur Verfügung hat, dann nutzt sie das auch und gibt nicht unbedingt immer etwas ans Betriebssystem frei.
 
Moin, danke für Deine Antwort. Nach einiger Zeit ist die App down und im sys.log steht drin, dass der Prozess gekilled wurde.

Habe das jetzt mal lokal angesehen, da fällt der ApplicationContect auf der recht groß ist, darin irgendwelche statsjson von Vaadin die sich aufsummieren mit der Zeit
1660372562072.png
 
Anhand der Informationen bin ich noch nicht von einem Speicherleck überzeugt. Der ApplicationContext ist das zentrale Singleton der Anwendung, klar ist der groß. Und das der Vaadin Krams drin hat, bei dem Zeug, was mit NPM Packages und Co macht, mag ok sein. Und 22 MB für ein Singelton sind Peanuts, bei einer Anwendung die Mindestens 1 GB, eher mehr zur Verfügung haben sollte.

Solche Probleme weiß ich leidgeprüft, sind extrem hässlich zu analysieren. Daher mal ein paar Tipps/Fragen:

  • Wer killt den Prozess warum?
  • Was steht im Log der Anwendung zu dem Zeitpunkt?
  • Wie ist der Speicher-Verbrauch der Anwendung über die Zeit?
  • Wie sind die Xmx und Co-Parameter?
  • Wie viel Speicher steht auf der Maschine zur Verfügung?

Sollte das noch passiert sein, wäre es jetzt dringend notwendig ein Monitoring der Anwendung aufsetzen, dass zentrale Performance-Parameter loggt. Stichwörter dazu microprofile metrics und dann wegschreiben in eine DB (Standard Tools in dem Umfeld sind wohl telegraph, influxdb, grafana und co)

Edit: https://spring.io/blog/2018/03/16/micrometer-spring-boot-2-s-new-application-metrics-collector - hier steht was zum Monitoring von Spring Boot, was schon out of the box eingebaut ist.

Das A und O bei sowas ist:
  • Informationen (und zwar im zeitlichen Ablauf, nicht wie hier reine Snapshots)
  • Jemanden der versteht, wie man die auswertet
 
Hallo Limdul, danke für deine Hilfe. Hier mal ein paar Antworten auf Deine Fragen. Wir haben grad noch @PWA entfernt, das hat den AppContect um 70% verkleinert. Mit dem PWA sind auch ein paar Ladeprobleme verschwunden, wir hoffen dass das auch mit dem RAM nun gelöst ist.

  • Wer killt den Prozess warum?
    Das OS killt den Prozess mit dem Hinweis "Memory Out" killed proces 1234 (exakte Fehlermeldung kann ich später noch mal hier einstellen, wenn es wieder auftaucht)

  • Was steht im Log der Anwendung zu dem Zeitpunkt?
    Leider nichts, ist einfach offline. Keine OutOfMemoryException o.ä.

  • Wie ist der Speicher-Verbrauch der Anwendung über die Zeit?
    Wächst kontinuierlich an, ohne RAM freizugeben

  • Wie sind die Xmx und Co-Parameter?
    die App startet über ein Linux .service mit folgenden Parametern
    ExecStart=/bin/sh -c "java -jar -Dserver.port=8080 -Djasypt.encryptor.password='xxx' -Dspring.profiles.active=live -Xms4g -Xmx4g /var/xxxx/app/app.jar >> /var/xxxx/app/app.log"

    haben aber auch schon xms=1G und xmx=4G getestet

  • Wie viel Speicher steht auf der Maschine zur Verfügung?
    5GB
 
Hast du die Applikation schon mal mit -XX:+HeapDumpOnOutOfMemoryError gestartet? Das könnte bei der ersten Analyse mehr helfen, als ein Heapdump irgendwann während der Laufzeit
 
Evtl. liege ich falsch, aber für mich hört sich das etwas nach einem System Problem an und nicht nach einem Java Problem. Wie ist die Speicherauslastung des gesamten Systems? Wenn das Java Programm von außen gekillt wird, dann wird es gekillt. Da wird auch eine Analyse nichts bringen fürchte ich.

Das Porgramm hat halt einen relativ hohen Speicherverbrauch (4GB) und ist damit immer ein Kandidat für den "Linux Out Of Memory Killer", den das System einsetzt, wenn es einen kritischen Speicherstand hat.
 
Das gesamte System ist zu 75% ausgelastet. Aufgrund der fehlenden OutOfMemory Exception sieht mir das auch so aus als wäre das OS überlastet. Das so gar nix im Log der Anwendung steht ist seltsam
 
Wenn das System von außen den Prozess killt, dann kann da nichts stehen. Wenn man Dir einen Kopfschuss verpasst, wirst Du Dich nicht auch noch verabschieden und allen Bescheid sagen: Sorry, ich bin dann jetzt mal weg, da ich einen Schuss in den Kopf bekommen habe.

OutOfMemory Exception ist ja auch nur dann, wenn etwas in der JavaVM mehr Speicher haben will und dieser nicht zur Verfügung steht.

Evtl. hilft Dir ja dies hier:
 
Ehm,. wo ich da mysqld sehe. Da läuft nicht ernsthaft noch ein Mysql-Server auf der Maschine? Das die dann abraucht ist ja kein Wunder.

5 GB Ram - davon der Java VM 4 GB erlaubt, bleibt noch 1 GB Ram für den gesamten Rest (bzw. eher weniger, weil afaik nutzt die JVM insgesamt mehr Speicher). Das heißt 1 GB für OS + Mysql + was auch sonst noch läuft ist extrem wenig. Gerade Datenbanken (sofern man mysql als solche bezeichnen will), sind extrem speicherhungrig.
 
Ja Mysql läuft da auch (später migrieren wir vermutlich zu Postgres, aktuell aber zu viel Aufwand). Es läuft bisher tadellos auf anderen Kisten. Nur die eine Instanz macht grad ein paar Problemchen.

Jetzt ohne die PWA Annotation bleibt der RAM bei stehen und wächst nicht mehr ins unendliche, sehr seltsam. soll es am PWA gelegen haben können?
 
hast du denn mal versucht , java weniger Speicher zu geben? wenn Du nur 5GB hast und du auch noch die Datenbank brauchst und so, dann macht da so nicht wirklich Sinn. Mal geschaut, ob der Server ggf mit 2GB klar kommen würde?

Ansonsten hilft da nur, dem System mehr Speicher zu geben.
 
Das Problem ist nicht deine Java-Anwendung, sondern der zur Verfügung stehende Speicher. Wenn du der Java VM 4 GB gibst, kann sein dass deine Java-Anwendung 3,8 GB Speicher reserviert, von diesen 3,8 GB aber bereits 2 GB durch den Garbage Collector abgeräumt werden könnten. Aber da die Java VM 4 GB nutzen darf, muss sie das nicht tun. Und dann geht dem OS der Speicher aus, weil das OS sieht ja nur die 3,8 GB die die Java gerade aktiv in Beschlag nimmt.

Das was du der Java VM als Höchstgrenze zugestehst, das musst du für das OS so kalkulieren, als würde das immer zu 100% ausgenutzt von der Java VM.
 
xmx gibt den maximalen Heap Speicher an. Mit -xmx2G weist Du java an, nicht mehr als 2GB Heap Speicher zu belegen. (Ein "von" gibt es dabei nicht. Das ist dann egal.)
 
Deine Maschine hat doch 5 GB sagtest du und die Parameter die du oben hattest, waren mit Xmx=4GB

Wenn du Xmx auf 2 Gigabyte setzt nutzt die VM nur 2 - aber nicht von 4 sondern von den 5
 
Noch mehr Lektüre, da ich mal davon ausgehe, dass bei 5 GB Ram es sich um einen Container handelt:

always make sure you allocate at least 25% more memory to your container (i.e. ‘-m’) than your heap size value
Da bist nur 4 GB Heap vs. 5 GB Ram exakt an der Grenze gewesen
 
Container ist es nicht direkt, also kein docker o.ä. ist ne VM (ist ja auch ne Art Container 😛)

Wir haben als Maßstab immer genommen: VM hat 4GB RAM, wir geben dem Heap 3GB also immer 1GB fürs OS. Reicht wohl nicht 🫡
 
Wie verhält es sich mit dem Input? Wir lesen da Dateien > 300MB via BufferedReader. Das geht doch auch auf den RAM oder drosselt die JVM die Lesegeschwindigkeit entsprechend dem verfügbaren RAM?
 
Wie verhält es sich mit dem Input? Wir lesen da Dateien > 300MB via BufferedReader. Das geht doch auch auf den RAM oder drosselt die JVM die Lesegeschwindigkeit entsprechend dem verfügbaren RAM?
So Du die Datei nicht als Ganzes in den Speicher lädst, ist das vollkommen egal. Die Frage ist halt, was Du da machst.
 
Ich lese die Daten Zeile für Zeile ein, jede Zeile wird in ein Objekt gewandelt und dann wird damit weiter gearbeitet (Das Einlesen macht ein Framework - UnivocityParser)
 
Also wenn Du dann die Objekte alle im Speicher hältst, dann wirst Du da natürlich entsprechend Speicher brauchen. Du kannst ja grob überschlagen, wieviel Speicher ein Objekt braucht um das dann mit der Anzahl zu multiplizieren. Das ist in der Regel ein vielfaches der Datei.

Aber wenn Du die Objekte nur erzeugst, etwas damit machst um sie dann "zu vergessen" und zur nächsten Zeile zu gehen, dann hast Du keinen dauerhaft belegten Speicher.
 

Zurück
Oben