Java sehr große Heap-Größen [geschlossen]


76

Hat jemand Erfahrung mit der Verwendung sehr großer Heaps, 12 GB oder höher in Java?

  • Macht der GC das Programm unbrauchbar?
  • Welche GC-Parameter verwenden Sie?
  • Welche JVM, Sun oder BEA wäre dafür besser geeignet?
  • Welche Plattform, Linux oder Windows, bietet unter solchen Bedingungen eine bessere Leistung?
  • Gibt es unter Windows einen Leistungsunterschied zwischen 64-Bit-Vista und XP bei solch hoher Speicherauslastung?

Antworten:


74

Wenn Ihre Anwendung nicht interaktiv ist und GC-Pausen für Sie kein Problem darstellen, sollte es für 64-Bit-Java kein Problem geben, sehr große Heaps zu verarbeiten, selbst in Hunderten von GB. Wir haben auch keine Stabilitätsprobleme unter Windows oder Linux bemerkt.

Wenn Sie jedoch die GC-Pausen niedrig halten müssen, wird es wirklich schlimm:

  1. Vergessen Sie den Standarddurchsatz, Stop-the-World-GC. Bei moderaten Heaps (<~ 30 GB) wird die Anwendung für einige zehn Sekunden und bei großen Heaps (> ~ 30 GB) für einige Minuten unterbrochen. Und der Kauf schnellerer DIMMs hilft nicht weiter.

  2. Die beste Wahl ist wahrscheinlich der CMS-Collector, der von -XX: + UseConcMarkSweepGC aktiviert wird. Der CMS-Garbage Collector stoppt die Anwendung nur für die anfängliche Markierungsphase und die Bemerkungsphase. Für sehr kleine Heaps wie <4 GB ist dies normalerweise kein Problem, aber für eine Anwendung, die viel Müll und einen großen Heap erzeugt, kann die Bemerkungsphase ziemlich lange dauern - normalerweise viel weniger als ein vollständiger Stopp der Welt , kann aber dennoch ein Problem für sehr große Haufen sein.

  3. Wenn der CMS-Garbage Collector nicht schnell genug ist, um den Betrieb zu beenden, bevor die dauerhafte Generation voll ist, greift er auf den Standard-Stop-the-World-GC zurück. Erwarten Sie ~ 30 oder mehr Sekunden lange Pausen für Haufen mit einer Größe von 16 GB. Sie können versuchen, dies zu vermeiden, indem Sie die langlebige Müllproduktionsrate Ihrer Anwendung so niedrig wie möglich halten. Beachten Sie, dass dieses Problem umso größer ist, je höher die Anzahl der Kerne ist, auf denen Ihre Anwendung ausgeführt wird, da das CMS nur einen Kern verwendet. Beachten Sie natürlich, dass es keine Garantie dafür gibt, dass das CMS nicht auf den STW-Kollektor zurückgreift. Und wenn dies der Fall ist, geschieht dies normalerweise bei Spitzenlasten, und Ihre Anwendung ist für einige Sekunden tot. Sie möchten wahrscheinlich keine SLA für eine solche Konfiguration signieren.

  4. Nun, da ist das neue G1-Ding. Es wurde theoretisch entwickelt, um die Probleme mit CMS zu vermeiden, aber wir haben es versucht und festgestellt, dass:

    • Der Durchsatz ist schlechter als der von CMS.
    • Es sollte theoretisch vermeiden, zuerst die populären Speicherblöcke zu sammeln, erreicht jedoch bald einen Zustand, in dem fast alle Blöcke "populär" sind und die Annahmen, auf denen es basiert, einfach aufhören zu funktionieren.
    • Schließlich gibt es für G1 immer noch den Stop-the-World-Fallback. Fragen Sie Oracle, wann dieser Code ausgeführt werden soll. Wenn sie "nie" sagen, fragen Sie sie, warum der Code dort ist. IMHO G1 lässt das riesige Heap-Problem von Java also wirklich nicht verschwinden, sondern macht es (wohl) nur ein wenig kleiner.
  5. Wenn Sie Geld für einen großen Server mit großem Speicher haben, haben Sie wahrscheinlich auch Geld für eine gute, kommerzielle, hardwarebeschleunigte, pausenlose GC-Technologie, wie die von Azul. Wir haben einen ihrer Server mit 384 GB RAM und es funktioniert wirklich gut - keine Pausen, 0 Zeilen Stop-the-World-Code im GC.

  6. Schreiben Sie den verdammten Teil Ihrer Anwendung, der in C ++ viel Speicher benötigt, wie es LinkedIn bei der Verarbeitung sozialer Grafiken getan hat. Sie werden dadurch immer noch nicht alle Probleme vermeiden (z. B. Heap-Fragmentierung), aber es wäre definitiv einfacher, die Pausen niedrig zu halten.


1
5. Unwahrscheinlich. Die 192-MB-Maschine kostet ca. 15.000 EUR. Azul Pricing ist Unternehmen, nicht wahr?
Stephan Eggermont

1
Dies ist leicht die beste Zusammenfassung hier. Ich möchte zwei Dinge hinzufügen: (1) Die CMSInitiatingOccupancyFraction kann das Problem "CMS kann nicht beendet werden, bevor das alte Gen voll ist" abmildern, aber (2) Im Gegensatz zum Durchsatzkollektor komprimiert CMS den Heap nicht, sodass die Fragmentierung normalerweise STW erzwingt GC schließlich.
Jbellis

@StephanEggermont du meintest 192 GB Maschine, oder?
Om-Nom-Nom

@ om-nom-nom ja, das stimmt. Kommentare können einen Tag später leider nicht bearbeitet werden
Stephan Eggermont

17

Ich bin CEO von Azul Systems, daher bin ich meiner Meinung nach zu diesem Thema offensichtlich voreingenommen! :) Davon abgesehen ...

Gil Tene, CTO von Azul, hat einen schönen Überblick über die mit der Garbage Collection verbundenen Probleme und einen Überblick über verschiedene Lösungen in seiner Präsentation Grundlegendes zur Java Garbage Collection und was Sie dagegen tun können. Weitere Informationen finden Sie in diesem Artikel: http: // www.infoq.com/articles/azul_gc_in_detail .

Azul's C4 Garbage Collector in unserer Zing JVM ist sowohl parallel als auch gleichzeitig und verwendet für die neue und die alte Generation denselben GC-Mechanismus, der in beiden Fällen gleichzeitig arbeitet und komprimiert. Am wichtigsten ist, dass C4 keinen Rückfall hat. Die gesamte Komprimierung wird gleichzeitig mit der laufenden Anwendung durchgeführt. Wir haben Kunden, die sehr groß sind (Hunderte von GByte), mit GC-Pausenzeiten im schlimmsten Fall von <10 ms und je nach Anwendung oft weniger als 1-2 ms.

Das Problem mit CMS und G1 besteht darin, dass der Java-Heapspeicher irgendwann komprimiert werden muss und beide Garbage Collectors die Welt stoppen / STW (dh die Anwendung anhalten), um die Komprimierung durchzuführen. Während CMS und G1 STW-Pausen auslösen können, werden sie nicht beseitigt. Azul's C4 eliminiert jedoch STW-Pausen vollständig und deshalb hat Zing selbst für gigantische Heap-Größen so niedrige GC-Pausen.


29
Nach ungefähr 6 E-Mails mit einem Ihrer Vertriebsmitarbeiter gab ich es auf, Preisinformationen zu erhalten. Eine Lösung, die Sie nicht einmal bewerten können, ist keine Lösung.
Chad Wilson

14

Wir haben eine Anwendung, für die wir 12-16 GB zuweisen, die aber im normalen Betrieb nur 8-10 erreicht. Wir verwenden die Sun JVM (haben IBMs ausprobiert und es war eine Katastrophe, aber das könnte unsererseits Unwissenheit gewesen sein ... Ich habe Freunde, die darauf schwören - die bei IBM arbeiten). Solange Sie Ihrer App Raum zum Atmen geben, kann die JVM große Heap-Größen mit nicht zu viel GC verarbeiten. Viel "zusätzlicher" Speicher ist der Schlüssel.
Linux ist fast immer stabiler als Windows und wenn es nicht stabil ist, ist es verdammt viel einfacher herauszufinden, warum. Solaris ist ebenfalls sehr solide und Sie erhalten auch DTrace :) Warum um alles in der Welt würden Sie mit diesen Lasten Vista oder XP verwenden? Sie bitten nur um Ärger. Mit den GC-Parametern machen wir nichts Besonderes. Wir setzen die minimale Zuordnung auf das Maximum, damit nicht ständig versucht wird, die Größe zu ändern, aber das ist es.


2
Ich würde nicht sagen, dass Linux stabiler als Windows war, aber es ist sehr wahrscheinlich, dass Sun-Tests JVM mehr auf Unit und Linex als auf Windows sind.
Ian Ringrose

9

Ich habe über 60 GB Heap-Größen in zwei verschiedenen Anwendungen unter Linux und Solaris verwendet, wobei (offensichtlich) 64-Bit-Versionen der Sun 1.6 JVM verwendet wurden.

Ich habe nie Probleme mit der Garbage Collection mit der Linux-basierten Anwendung festgestellt, außer wenn ich mich der Heap-Größenbeschränkung näherte. Um die mit diesem Szenario verbundenen Probleme zu vermeiden (zu viel Zeit für die Speicherbereinigung), habe ich einfach die Speichernutzung im gesamten Programm so optimiert, dass die maximale Auslastung etwa 5 bis 10% unter einer Heap-Größenbeschränkung von 64 GB lag.

Bei einer anderen Anwendung, die unter Solaris ausgeführt wird, sind jedoch erhebliche Probleme bei der Speicherbereinigung aufgetreten, die viele Optimierungen erforderlich machten. Dies bestand hauptsächlich aus drei Schritten:

  1. Aktivieren / Erzwingen der Verwendung des parallelen Garbage Collector über die JVM-Optionen -XX: + UseParallelGC -XX: + UseParallelOldGC sowie Steuern der Anzahl der über die Option -XX: ParallelGCThreads verwendeten GC-Threads. Weitere Informationen finden Sie unter " Optimierung der Garbage Collection für Java SE 6 HotSpot Virtual Machine ".

  2. Umfangreiche und scheinbar lächerliche Einstellung lokaler Variablen auf "null", nachdem sie nicht mehr benötigt werden. Bei den meisten dieser Variablen handelte es sich um Variablen, die nach dem Verlassen des Gültigkeitsbereichs für die Speicherbereinigung in Frage gekommen sein sollten, und es handelte sich nicht um Speicherverlustsituationen, da die Referenzen nicht kopiert wurden. Diese "Hand-Holding" -Strategie zur Unterstützung der Speicherbereinigung war jedoch aus irgendeinem Grund für diese Anwendung unter der fraglichen Solaris-Plattform unerklärlich erforderlich.

  3. Selektive Verwendung des System.gc () -Methodenaufrufs in Schlüsselcodeabschnitten nach längeren Zeiträumen temporärer Objektzuweisung. Ich bin mir der üblichen Vorbehalte gegen die Verwendung dieser Aufrufe und des Arguments bewusst, dass sie normalerweise unnötig sein sollten, aber ich fand, dass sie bei der Zähmung der Speicherbereinigung beim Ausführen dieser speicherintensiven Anwendung von entscheidender Bedeutung sind.

Die drei oben genannten Schritte machten es möglich, diese Anwendung in einer Heap-Auslastung von etwa 60 GB zu halten und produktiv auszuführen, anstatt außer Kontrolle zu geraten, bis die bestehende Heap-Größenbeschränkung von 128 GB erreicht war. Insbesondere der parallele Garbage Collector war sehr hilfreich, da große Garbage Collection-Zyklen teuer sind, wenn viele Objekte vorhanden sind, dh die für die große Garbage Collection erforderliche Zeit hängt von der Anzahl der Objekte im Heap ab.

Ich kann weder andere plattformspezifische Probleme in dieser Größenordnung kommentieren noch JVMs verwenden, die nicht von Sun (Oracle) stammen.


8

12 GB sollten mit einer anständigen JVM-Implementierung wie Suns Hotspot kein Problem sein. Ich würde Ihnen raten, den Concurrent Mark and Sweep-Kolllektor (-XX: + UseConcMarkSweepGC) zu verwenden, wenn Sie eine SUN VM verwenden. Andernfalls kann es zu langen "Stop the World" -Phasen kommen, in denen alle Threads während eines GC gestoppt werden.

Das Betriebssystem sollte keinen großen Unterschied für die GC-Leistung machen.

Sie benötigen natürlich ein 64-Bit-Betriebssystem und einen Computer mit genügend physischem RAM.


7

Ich empfehle auch, einen Heap-Dump zu erstellen und zu prüfen, wo die Speichernutzung in Ihrer App verbessert werden kann, und den Dump in etwas wie Eclipse's MAT zu analysieren . Auf der MAT-Seite finden Sie einige Artikel zum Einstieg in die Suche nach Speicherlecks. Sie können jmap verwenden, um den Speicherauszug mit etwas wie ...

jmap -heap:format=b pid

... und wie beantwortet dies die eigentliche Frage?
ddimitrov

3
Denn bei einer so großen Heap-Größe sollten Sie
versuchen

Zustimmen. Sofern Sie keine ganz besondere Art von Anwendung haben, sollten Sie keine 12 GB Heap benötigen. Das deutet normalerweise auf schlechte Codierungspraktiken hin, z. B. das gleichzeitige Laden großer Dinge in den Arbeitsspeicher, die Sie stattdessen streamen sollten. Wenn Sie das richtig machen, lässt sich Ihre Anwendung auch gut skalieren. Wenn Sie es falsch machen, müssen Sie Ihre Heap-Größe weiter erhöhen, wenn Ihre App geschäftiger wird / größere Datenmengen verarbeitet.
Frans

2

Wie oben erwähnt, sollte der standardmäßige (komprimierende) Garbage Collector (GC) gut funktionieren, wenn Sie ein nicht interaktives Programm haben. Wenn Sie ein interaktives Programm haben und (1) nicht schneller Speicher zuweisen, als der GC mithalten kann, und (2) keine temporären Objekte (oder Sammlungen von Objekten) erstellen, die zu groß sind (im Verhältnis zur Gesamtsumme) maximaler JVM-Speicher), damit der GC umgehen kann, dann ist CMS für Sie.

Sie haben Probleme, wenn Sie ein interaktives Programm haben, bei dem der GC nicht genügend Raum zum Atmen hat. Das gilt unabhängig davon, wie viel Speicher Sie haben, aber je mehr Speicher Sie haben, desto schlimmer wird es. Dies liegt daran, dass CMS nicht genügend Arbeitsspeicher zur Verfügung steht, wenn der Arbeitsspeicher zu niedrig ist, während die komprimierenden GCs (einschließlich G1) alles anhalten, bis der gesamte Arbeitsspeicher auf Müll überprüft wurde. Diese Stop-the-World-Pause wird größer, je mehr Speicher Sie haben. Vertrauen Sie mir, Sie möchten nicht, dass Ihre Servlets länger als eine Minute pausieren. Ich habe eine detaillierte StackOverflow-Antwort zu diesen Pausen in G1 geschrieben.

Seitdem ist meine Firma zu Azul Zing gewechselt. Es kann immer noch nicht mit dem Fall umgehen, dass Ihre App wirklich mehr Speicher benötigt als Sie haben, aber bis zu diesem Moment läuft es wie ein Traum.

Aber natürlich ist Zing nicht kostenlos und seine spezielle Sauce ist patentiert. Wenn Sie weit mehr Zeit als Geld haben, schreiben Sie Ihre App neu, um einen Cluster von JVMs zu verwenden.

Am Horizont arbeitet Oracle an einem Hochleistungs-GC für Multi-Gigabyte-Heaps. Ab heute ist dies jedoch keine Option.


1

Wenn Sie zu 64-Bit wechseln, benötigen Sie mehr Speicher. Zeiger werden zu 8 Bytes anstelle von 4. Wenn Sie viele Objekte erstellen, kann dies auffallen, da jedes Objekt eine Referenz (Zeiger) ist.

Ich habe kürzlich 15 GB Speicher in Java mit der Sun 1.6 JVM ohne Probleme zugewiesen. Obwohl alles nur einmal vergeben wird. Nach dem anfänglichen Betrag wird nicht viel mehr Speicher zugewiesen oder freigegeben. Dies war unter Linux, aber ich kann mir vorstellen, dass die Sun JVM unter 64-Bit-Windows genauso gut funktioniert.


1

Sie sollten versuchen, visualgc für Ihre App auszuführen. Es ist ein Heap-Visualisierungstool, das Teil des jvmstat-Downloads unter http://java.sun.com/performance/jvmstat/ ist.

Es ist viel einfacher als das Lesen von GC-Protokollen.

Es hilft Ihnen schnell zu verstehen, wie die Teile (Generationen) des Heaps funktionieren. Während Ihr gesamter Heap 10 GB betragen kann, sind die verschiedenen Teile des Heaps viel kleiner. GCs im Eden-Teil des Haufens sind relativ billig, während volle GCs in der alten Generation teuer sind. Es ist eine gute Strategie, Ihren Haufen so zu dimensionieren, dass das Eden groß ist und die alte Generation kaum berührt wird. Dies kann zu einem sehr großen Gesamthaufen führen, aber was solls, wenn die JVM die Seite nie berührt, ist sie nur eine virtuelle Seite und muss keinen RAM belegen.


1

Vor ein paar Jahren habe ich JRockit und die Sun JVM für einen 12G-Haufen verglichen. JRockit hat gewonnen und die Unterstützung von Linux-Riesen-Seiten hat unseren Testlauf um 20% beschleunigt. YMMV als unser Test war sehr prozessor- / speicherintensiv und war hauptsächlich Single-Threaded.


Welche Java-Version war das, und hätten Sie heute Zeit, es noch einmal zu tun? Die Zahlen wären sehr interessant.
Thorbjørn Ravn Andersen

Ich berate nicht mehr für dasselbe Unternehmen, daher habe ich nicht einmal die Umgebung, um dies auszuprobieren. Es war ein JDK1.5 JRockit, IIRC.
ShabbyDoo

1

Hier ist ein Artikel über gc von einem der Java Champions - http://kirk.blog-city.com/is_your_concurrent_collector_failing_you.htm

Kirk, der Autor, schreibt: "Schicken Sie mir Ihre GC-Protokolle

Ich bin derzeit daran interessiert, von Sun JVM produzierte GC-Protokolle zu studieren. Da diese Protokolle keine geschäftsrelevanten Informationen enthalten, sollten Bedenken hinsichtlich des Schutzes proriatärer Informationen ausgeräumt werden. Ich bitte Sie nur, mit dem Protokoll das Betriebssystem, die vollständigen Versionsinformationen für die JRE und alle von Ihnen festgelegten Heap / GC-bezogenen Befehlszeilenoptionen zu erwähnen. Ich würde auch gerne wissen, ob Sie Grails / Groovey, JRuby, Scala oder etwas anderes als oder neben Java ausführen. Die beste Einstellung ist -Xloggc:. Bitte beachten Sie, dass dieses Protokoll nicht verlängert wird, wenn es die Größenbeschränkung Ihres Betriebssystems erreicht. Wenn ich etwas Interessantes finde, gebe ich Ihnen gerne eine sehr kurze Zusammenfassung. ""




0

Der maximale Speicher, den XP adressieren kann, beträgt 4 Gig ( hier ). Daher möchten Sie möglicherweise kein XP dafür verwenden (verwenden Sie ein 64-Bit-Betriebssystem).


Oder verwenden Sie die 64-Bit-Version von XP. ;)
Tyler Millican

Dies ist keine Einschränkung von XP, sondern eine Einschränkung eines 32-Bit-Betriebssystems, das kein PAE verwendet.
TM.

1
Dies ist eine Einschränkung aller 32-Bit-Betriebssysteme, auch derjenigen, die PAE verwenden.
James

@james, Wenn Sie eine PAE verwenden, werden die gesamten 4 GB angezeigt. Wenn Sie keine PAE haben, werden keine Geräte angezeigt, die dem Speicher zugeordnet sind (Grafikkarten usw.).
Milhous

0

sun hat seit einiger Zeit ein 64-Bit-JVM von itanium, obwohl itanium kein beliebtes Ziel ist. Die 64-Bit-JVMs von Solaris und Linux sollten genau das sein, wonach Sie suchen sollten.
Einige Fragen

1) Ist Ihre Anwendung stabil?
2) Haben Sie die App bereits in einer 32-Bit-JVM getestet?
3) Ist es in Ordnung, mehrere JVMs auf derselben Box auszuführen?

Ich würde erwarten, dass das 64-Bit-Betriebssystem von Windows in ungefähr einem Jahr stabil wird, aber bis dahin ist Solaris / Linux möglicherweise die bessere Wahl.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.