Java Heap (Memory-Volume) vergrößern – Windows
Bist du schon mal an ein Speicherlimit in Java gestoßen und wurdest mit der Fehlermeldung „java.lang.OutOfMemoryError: Java heap space“ begrüßt? Ärgerlich, nicht wahr! Aber das kann man ändern!
Speicher? Wer achtet noch auf Speicher? Davon haben wir doch heutzutage genug! Vorbei sind die Zeiten mit 16MB EdoRAM – wobei es mir kalt den Rücken runter läuft. Heute sind 4 GB Standard, wenn nicht sogar 8 GB. Aber was wenn deiner Programmiersprache relativ egal ist wie viel Speicher du besitzt, weil diese gedeckelt ist?
Genau das ist mir in der letzten Woche passiert. ich wollte ein Array erstellen, welches locker mal so 1000 x 1000 x 1000 Integer-Werte enthalten sollte. Ja ich gebe es zu, ich bin die Sache relativ naiv angegangen, denn das wäre abzusehen gewesen. Daher möchte ich heute zeigen, wie man Java dazu bringt – bei Bedarf – mehr Speicher zu nutzen.
Zwei Varianten um den Java Heap zu vergrößern
- Heap vergrößern in Eclipse, speziell für eine Klasse
- Heap vergrößern in dem auszuführenden .JAR-File, per Batchaufruf
Vorab: Der Code
Vorab möchte ich – dass das Beispiel nicht allzu theoretisch wirkt – ein Code-Schnipsel zeigen. Bei diesem wird ein Integer-Array mit drei Dimensionen angelegt, jede mit einer Größe von 1000 Werten. Im Anschluss wird in jede Array-Größe ein Wert von 1 geschrieben.
//größe einer Array-Dimension
int arraySize=1000;
//Dimensions Counter zur Überprüfung
long l=0;
//Array initialisieren
int[][][]a=new int[arraySize][arraySize][arraySize];
//Alle Dimensionen durch gehen
for(int x=0;x<arraySize;x++){
for(int y=0;y<arraySize;y++){
for(int z=0;z<arraySize;z++){
//Dimension mit 1 belegen
a[x][y][z]=1;
//counter hochzählen
l++;
}
}
}
//wie viele Dimensionen wurden beschrieben
System.out.println(l);
Verhängnisvolles Kubik
Anfänglich sieht das ganze harmlos aus, doch bei genauer Berechnung stellt sich heraus, dass diese Annahme naiv ist. Ein Integer-Wert ist in Java mit 32 Bit umgrenzt. Das heißt, bei 1000³ (1000 * 1000 * 1000) Werten ergibt das 32*10³ (32.000.000.000) Bit. Das ganze durch 8 (Byte), dann durch 1024 (KByte) und nochmal durch 1024 (MByte) ergibt einen Speicherbedarf von 3.814 MB (3,72 GB). … wenn ich mir das so anschaue ist das mehr als naiv!
ArraySize | ArraySize³ | Speichervolumen in MB |
---|---|---|
200 | 8.000.000 | 30 |
512 | 134.217.728 | 512 |
600 | 216.000.000 | 823 |
800 | 512.000.000 | 1.953 |
1000 | 1.000.000.000 | 3.814 |
Dazu gibt es natürlich auch wieder ein Berechnungscode. Die Sourcecode-Dateien sind wie immer im Anhang hinterlegt!
/**
* Gibt das Speichervolumen in MB zurück
*
* @param size(count of values)
* @return size memoryvolume(in MB)
*/
public long calcIntegerHeap(long size){
// Bit-size
long l=32*size;
System.out.println("b:"+l);
// Byte-size
l=l/8;
System.out.println("B:"+l);
// KByte-size
l=l/1024;
System.out.println("KB:"+l);
// MB-size
l=l/1024;
System.out.println("MB:"+l);
// count of values
System.out.println(size+"values");
// rückgabe in MB
return l;
}
Begrifflichkeiten: Xms und Xmx
Vorab müssen wir noch kurz zwei Begrifflichkeiten klären. Xms gibt das Standardvolumen an, mit dem Eclipse bzw. ein Java Programm gestartet wird. Dieser Speicher wird für das Javaprogramm reserviert. Xmx gibt das Maximum an, die sogenannte Deckelung.
Java Heap vergrößern in Eclipse
In Eclipse ist es möglich pro Klasse einen minimalen (Xms) und maximalen (Xmx) Speicherbereich zu definieren. Dazu rufst du den Menüpunkt „Run“>“Run Configurations“ auf. Unter „Java Application“ und der gewünschten Klasse wählst du den Reiter „Arguments“.
Unter „VM arguments“ hast du nun die Möglichkeit, deinem Programm Startparameter mitzugeben. Diese würden eigentlich beim Aufruf der fertigen JAR-Datei mitgegeben (siehe Unten „direkter Aufruf des .JAR-File mit Batchdatei“), jedoch wäre es etwas nervig diese immer wieder zu erstellen und auszuführen. So bringt Eclipse diese Funktion von Haus aus mit.
Trage nun im Feld „VM arguments“ folgende Zeilen ein:
- Xms64m
- Xmx512m
und fertig sind wir. Mit den zwei Zeilen sagen wir Eclipse nun, dass wir mindestens 64MB reserviert haben wollen, aber die Deckelung des Speichers bei 512 MB (0,5 GB) liegt. Das heißt auch, dass unser Array jetzt mit 512 Werten pro Dimension initialisiert werden kann. Ok, vielleicht sollte man ein paar weniger Werte nehmen, denn der Long Counter benötigt auch noch ein paar Speicher Ressourcen. Die Werte können jetzt beliebig auch nach oben angepasst werden. Probiere mal den Xmx Wert von 4000 und führe das Array mit einer jeweiligen Dimensionsgröße von 1000 aus!
Direkter Aufruf des .JAR-File mit Batchdatei
Kommen wir noch zum letzten Punkt. Wenn das Java Programm nun irgendwann fertig ist und wir es ausliefern wollen, ohne Eclipse Umgebung, müssen wir dem Programm ebenfalls mitteilen, dass es die Speicherdeckelung hochfährt. Dazu speicherst du die Datei als ausführbares .JAR-File in einen beliebigen Ordner (bspw. heap512.jar). Leg nun eine Textdatei (run.txt) neben das JAR-File. In diese schreibst du folgende Zeilen:
java -Xmx512M -Xms64M -jar heap512.jar
PAUSE
Damit rufen wir manuell unser Java-Programm auf und geben ihm wieder beide Parameter mit. In der zweiten Zeile geben wir noch ein „PAUSE“-Command mit. Dies bezweckt, dass die Konsole – welche als Ausgabe dient – nicht sofort nach Abarbeitung des Programms verschwindet.
Anschließend benennen wir die Datei noch in „run.bat“ um. Das ist wichtig, da sonst Windows nicht versteht, dass es sich um eine ausführbare Batchdatei handelt. Achte darauf, dass dir bei der Textdatei die Ändung mit angezeigt wird (die zumeist ausgeblendet wird), ansonsten erhälst du nach dem Umbenennen in Wirklichkeit eine „run.bat.txt“ Datei und das File bleibt unausführbar.
Download
- ArrayHeap und Heapberechnung: Javasource-Heap
- JAR-Export und Batchdatei: JAR-Batch
Kann es sein, dass zwischen dem Minus und dem Argument in Eclipse kein Leerzeichen sein darf? Ich habe deine Argumente nämlich mal kopiert und bekam eine Exception. Ohne Leerzeichen funktioniert es allerdings.
Bei mir hat es so funktioniert. Aber vielleicht ist es eine Versions-Geschichte?