Diese SO-Antwort erläutert einige Dinge zum -Xmx
JVM-Flag. Versuche zu experimentieren habe ich folgendermaßen gemacht:
Kompiliere es mit javac FooMain.java
. Wenn ich es mit einer maximalen Heap-Größe von 5 Millionen Bytes ausführe, bekomme ich:
Obwohl die Zahlen nah genug sind, scheinen sie nicht sehr genau zu sein (mit Ausnahme von total memory
am Ende, das genau max memory
erreicht). Insbesondere 5368 Arrays mit je 1000 Byte sollten mehr als 5000000 oder sogar 5242880 Byte benötigen. Wie sollen diese Zahlen verstanden werden?
Dies ist das Java, das ich verwende:
%Vor%ist -Xmx eine harte Grenze?
Es kommt darauf an, was du mit "hart" meinst. Wenn du meinst "Kann nicht vom Programm geändert werden", dann Ja. Wenn Sie "präzise" meinen, dann Nein. Die Größe der verschiedenen Räume, die der Garbage Collector verwendet, ist ein Vielfaches einer Potenz von 2 Bytes. Und anscheinend rollt die JVM eher nach oben als nach unten.
Beispielprogramm, das 1000-Byte-Arrays zuordnet
Wie sollen diese Zahlen verstanden werden?
Ein 1000-Byte-Java-Array belegt nicht genau 1000 Bytes:
Es gibt eine Objektkopfzeile, einschließlich Platz für ein 32-Bit-Feld length
. (In der Regel 3 x 32 Bit).
Heap-Knoten werden in Vielfachen von 2 ^ N Bytes zugewiesen. (In der Regel 2 ^ 3 == 8)
Dann hast du die Frage, was die OutOfMemoryError
verursacht. Sie könnten denken, dass dies daran liegt, dass der Heap vollständig voll ist. In diesem Fall lautet die Meldung jedoch "GC Overhead Limit überschritten". Das bedeutet, dass die JVM festgestellt hat, dass die JVM einen zu großen Prozentsatz der gesamten CPU-Zeit aufwendet, die den Garbage Collector ausführt. Das hat den GC getötet ... nicht genug Speicher.
Der Grund für das "GC-Overhead-Limit" ist, dass wenn ein Heap voll wird, der GC häufig läuft und immer weniger Speicher beansprucht. Wenn Sie in eine GC "Todesspirale" geraten, ist es besser, den Stopfen schnell zu ziehen, anstatt der Anwendung zu erlauben, bis zum endgültigen Punkt des Versagens zu schleifen.
Wie auch immer ... das bedeutet, dass Ihre Heuristik, um herauszufinden, wie viel Speicher zugeordnet wird, wenn der Heap voll ist, wahrscheinlich falsch ist.
Wenn Sie sich den OpenJDK-Quellcode ansehen, ist die Logik zum Ermitteln der maximalen Heap-Größe ziemlich komplex und wird von vielen Variablen bestimmt. Die tatsächliche Größe des Heapspeichers wird in hotspot/src/share/vm/memory/collectorPolicy.cpp
festgelegt, verwendet den angegebenen -Xmx-Wert als Eingabe und richtet ihn unter Verwendung des folgenden Codes aus:
align_size_up ist definiert als:
%Vor%Und max_alignment ist das Produkt der Seitengröße des virtuellen Speichers und der Größe der JVM-Speicherbereinigungskarte. Die VM-Seitengröße beträgt 4096 Byte und die Kartengröße beträgt 512 Byte. Wenn Sie diese Werte einstecken, erhalten Sie eine tatsächliche MaxHeapSize von 6324224 Byte, die gut mit den Zahlen übereinstimmt, die Sie sehen.
Hinweis: Ich habe nur kurz in den JVM-Code geschaut, daher ist es möglich, dass ich etwas übersehen habe, aber die Antwort scheint sich mit dem zusammenzufassen, was Sie sehen.
Ihre Frage wird inzwischen von anderen Leuten so ziemlich beantwortet, aber aus Interesse habe ich die Mathematik gemacht: -)
Es heißt hier :
Dieser Wert muss ein Vielfaches von 1024 größer als 2 MB sein.
5000000 ist kein Vielfaches von 1024. Meine Schätzung war, dass das Argument auf ein Vielfaches von 1024 aufgerundet wird, was die anderen Antworten bestätigen. Ihr gesamter Speicher (der mehr als nur der angegebene Heap-Speicherbereich ist) ist 5767168, also 5632 * 1024
. Es wurde während der Laufzeit von der ursprünglichen 5242880 auf den wachsenden Heap-Speicherplatz erweitert. Beachten Sie, dass -Xmx
das Maximum deklariert, sodass es nicht unbedingt sofort zugewiesen wird. Mit Hilfe von dieser Quelle können wir Ihre Speicherbelegung ermitteln (unter der Annahme von 64bit):
4878000
Bytes für die Bytes 4878 * 24 = 117072
bytes Array-Aufwand Also nehmen die Arrays 4995072 Bytes auf, was (zufälligerweise?) schon ein Vielfaches von 1024 wäre. Aber es gibt immer noch den Overhead für die anderen Objekte. Der Heap-Speicherplatz ist also ein Vielfaches von 1024 größer als 4995072 und kleiner 5767168. Wenn wir den freien Platz am Ende betrachten, bleiben 228808 Byte für den Rest der Heap- und Nicht-Heap-Speicherbelegung, was wie eine plausible Zahl klingt.
Endlich ein Wort zum freien Platz am Ende. Die JVM füllt nicht notwendigerweise den gesamten Speicher auf, bevor sie abstürzt. Wenn der Arbeitsspeicher knapp wird, wird die Garbage Collection häufiger ausgeführt. Wenn dies einen bestimmten Prozentsatz der Gesamtlaufzeit beansprucht, endet die JVM , was in deinem Fall passiert ist.