Dies ist ein Problem, das ich seit einigen Monaten aufzuspüren versuche. Ich habe eine Java-App ausgeführt, die XML-Feeds verarbeitet und das Ergebnis in einer Datenbank speichert. Es gab zeitweise Ressourcenprobleme, die nur sehr schwer aufzuspüren sind.
Hintergrund: Auf der Produktionsbox (wo das Problem am deutlichsten auftritt) habe ich keinen besonders guten Zugriff auf die Box und konnte Jprofiler nicht zum Laufen bringen. Diese Box ist eine 64-Bit-Quad-Core-Maschine mit 8 GB und Centos 5.2, Tomcat6 und Java 1.6.0.11. Es beginnt mit diesen Java-Optionen
JAVA_OPTS="-server -Xmx5g -Xms4g -Xss256k -XX:MaxPermSize=256m -XX:+PrintGCDetails -
XX:+PrintGCTimeStamps -XX:+UseConcMarkSweepGC -XX:+PrintTenuringDistribution -XX:+UseParNewGC"
Der Technologie-Stack lautet wie folgt:
- Centos 64-Bit 5.2
- Java 6u11
- Kater 6
- Spring / WebMVC 2.5
- Ruhezustand 3
- Quarz 1.6.1
- DBCP 1.2.1
- MySQL 5.0.45
- Ehcache 1.5.0
- (und natürlich eine Vielzahl anderer Abhängigkeiten, insbesondere die Jakarta-Commons-Bibliotheken)
Das Problem, das ich am ehesten reproduzieren kann, ist ein 32-Bit-Computer mit geringerem Speicherbedarf. Dass ich die Kontrolle habe. Ich habe es mit JProfiler zu Tode geprüft und viele Leistungsprobleme behoben (Synchronisationsprobleme, Vorkompilieren / Zwischenspeichern von xpath-Abfragen, Reduzieren des Threadpools und Entfernen unnötigen Vorabrufs im Ruhezustand und übereifriges "Cache-Erwärmen" während der Verarbeitung).
In jedem Fall zeigte der Profiler, dass diese aus dem einen oder anderen Grund große Mengen an Ressourcen in Anspruch nahmen und dass es sich nach den Änderungen nicht mehr um primäre Ressourcenfresser handelte.
Das Problem: Die JVM scheint die Einstellungen für die Speichernutzung vollständig zu ignorieren, füllt den gesamten Speicher und reagiert nicht mehr. Dies ist ein Problem für den Kunden, der eine regelmäßige Umfrage erwartet (5-Minuten-Basis und 1-Minuten-Wiederholungsversuch), sowie für unsere Betriebsteams, die ständig benachrichtigt werden, dass eine Box nicht mehr reagiert und neu gestartet werden muss. Auf dieser Box läuft nichts anderes.
Das Problem scheint die Speicherbereinigung zu sein. Wir verwenden den ConcurrentMarkSweep-Kollektor (wie oben angegeben), da der ursprüngliche STW-Kollektor JDBC-Timeouts verursachte und zunehmend langsamer wurde. Die Protokolle zeigen, dass mit zunehmender Speichernutzung cms-Fehler auftreten und der ursprüngliche Stop-the-World-Kollektor wiederhergestellt wird, der dann anscheinend nicht richtig erfasst wird.
Wenn Sie jedoch mit jprofiler arbeiten, scheint die Schaltfläche "GC ausführen" den Speicher gut zu bereinigen, anstatt einen zunehmenden Platzbedarf anzuzeigen. Da ich jprofiler jedoch nicht direkt mit der Produktionsbox verbinden kann und das Auflösen bewährter Hotspots anscheinend nicht funktioniert, bin ich es links mit dem Voodoo der Garbage Collection blind zu tunen.
Was ich versucht habe:
- Profilerstellung und Behebung von Hotspots.
- Verwenden von STW-, Parallel- und CMS-Garbage Collectors.
- Laufen mit minimalen / maximalen Heap-Größen in Schritten von 1 / 2,2 / 4,4 / 5,6 / 6.
- Laufen mit Permgen-Speicherplatz in Schritten von 256 MB bis zu 1 GB.
- Viele Kombinationen der oben genannten.
- Ich habe auch die JVM [Tuning-Referenz] konsultiert (http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html), kann aber nichts finden, was dieses Verhalten erklärt, oder Beispiele für die Optimierung Parameter, die in einer solchen Situation verwendet werden sollen.
- Ich habe auch (erfolglos) versucht, jprofiler im Offline-Modus mit jconsole, visualvm zu verbinden, aber ich kann anscheinend nichts finden, was meine gc-Protokolldaten stören könnte.
Leider tritt das Problem auch sporadisch auf, es scheint unvorhersehbar zu sein, es kann tagelang oder sogar eine Woche lang ohne Probleme laufen oder es kann 40 Mal am Tag ausfallen, und das einzige, was ich konsequent zu fangen scheint, ist Diese Speicherbereinigung wirkt.
Kann jemand einen Rat geben zu:
a) Warum eine JVM 8 physische Gigs und 2 GB Swap Space verwendet, wenn sie so konfiguriert ist, dass sie maximal 6 beträgt.
B) Ein Verweis auf die GC-Optimierung, der tatsächlich erklärt oder vernünftige Beispiele gibt von wann und mit welcher Einstellung die erweiterten Sammlungen verwendet werden sollen.
c) Ein Verweis auf die häufigsten Java-Speicherlecks (ich verstehe nicht beanspruchte Verweise, aber ich meine auf Bibliotheks- / Framework-Ebene oder etwas, das in Datenstrukturen inhärenter ist, wie z. B. Hashmaps).
Vielen Dank für alle Einblicke, die Sie gewähren können.
EDIT
Emil H:
1) Ja, mein Entwicklungscluster ist ein Spiegel der Produktionsdaten bis hinunter zum Medienserver. Der Hauptunterschied ist das 32/64-Bit und die verfügbare RAM-Größe, die ich nicht so einfach replizieren kann, aber der Code sowie die Abfragen und Einstellungen sind identisch.
2) Es gibt einen Legacy-Code, der auf JaxB basiert. Bei der Neuordnung der Jobs, um Planungskonflikte zu vermeiden, wird diese Ausführung jedoch im Allgemeinen eliminiert, da sie einmal am Tag ausgeführt wird. Der primäre Parser verwendet XPath-Abfragen, die das Paket java.xml.xpath aufrufen. Dies war die Quelle einiger Hotspots, zum einen wurden die Abfragen nicht vorkompiliert, und zum anderen befanden sich die Verweise auf sie in fest codierten Zeichenfolgen. Ich habe einen threadsicheren Cache (Hashmap) erstellt und die Verweise auf die xpath-Abfragen als endgültige statische Zeichenfolgen berücksichtigt, wodurch der Ressourcenverbrauch erheblich gesenkt wurde. Die Abfrage ist immer noch ein großer Teil der Verarbeitung, aber es sollte sein, dass dies die Hauptverantwortung der Anwendung ist.
3) Ein weiterer Hinweis: Der andere Hauptverbraucher sind Bildoperationen von JAI (Wiederaufbereitung von Bildern aus einem Feed). Ich bin mit den Grafikbibliotheken von Java nicht vertraut, aber soweit ich festgestellt habe, sind sie nicht besonders undicht.
(Danke für die bisherigen Antworten, Leute!)
UPDATE:
Ich konnte mit VisualVM eine Verbindung zur Produktionsinstanz herstellen, aber die Option GC-Visualisierung / Run-GC wurde deaktiviert (obwohl ich sie lokal anzeigen konnte). Das Interessante: Die Heap-Zuordnung der VM entspricht JAVA_OPTS, und der tatsächlich zugewiesene Heap sitzt bequem bei 1-1,5 Gigs und scheint nicht zu lecken, aber die Überwachung auf Box-Ebene zeigt immer noch ein Leckmuster, aber es ist wird in der VM-Überwachung nicht berücksichtigt. Es läuft nichts anderes auf dieser Box, also bin ich ratlos.