Heap-Volllaufen bei ArrayList<Integer>

Hallo,

ich habe einen Engpass in der folgenden Routine: Ich möchte aus einer relationalen Datenbank, die wiefolgt aufgebaut ist, "ein paar" Datensätze in eine Liste speichern.
- Wald
- Baum
- Ast
- Zweig
- Blatt

Dabei hole ich mir ein paar bestimmte Bäume aus dem Wald, die dazugehörigen Äste, Zweige und auch Blätter und speichere diese entsprechend in einer ArrayList<Integer> baeume, aeste etc. Der Integer ist dabei der PRIMARY KEY (der Index) des jeweiligen Datensatzes.

Nun kann es sein, dass ich bis zu 1.000 Bäume aus dem Wald holen muss, die in dem Falle wiederum mindestens 500 Äste haben, die wiederum (...). Also eine ganze Anzahl an Indizes, die gespeichert werden müssen (die Anzahl der Äste ist ~500x1000, die der Zweige ~500x1000x4 usw.).

Nun bin ich einher gegangen und habe einen Profiler (VisualVM) verwendet, um mir die "großen" Objekte anzeigen zu lassen. Dabei ist kurz vor dem heap-space-Volllaufen folgendes Bild entstanden: http://img407.imageshack.us/img407/1049/heap.png

Die Exception lautet wiefolgt
Code:
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
	at java.util.HashMap.<init>(Unknown Source)
	at java.util.HashSet.<init>(Unknown Source)

Nun zu meiner eigentlichen Frage: Das Objekt java.util.HashMap$Entry[] ist laut Profiling "bloß" ~4MB groß, der Rest nicht viel größer; wieso läuft mir also der Heap voll?

Ich programmiere nicht allzu viel in Java, also bitte ich um etwas Nachsicht; hoffe aber, dass mir geholfen werden kann. Es gibt selbstverständlich weitere Lösungsansätze zu diesem Thema, partielle Bestimmung der Indizes usw. aber darum geht es mir nicht. Ich versuche eher gerade die Problematik zu verstehen.



LG KK
 
Du wirst deiner Applikation mehr Speicher zuweisen müssen:
-Xmsn
Specify the initial size, in bytes, of the memory allocation pool. This value must be a multiple of 1024 greater than 1MB. Append the letter k or K to indicate kilobytes, or m or M to indicate megabytes. The default value is chosen at runtime based on system configuration. For more information, see HotSpot Ergonomics
Examples:

-Xms6291456
-Xms6144k
-Xms6m


-Xmxn
Specify the maximum size, in bytes, of the memory allocation pool. This value must a multiple of 1024 greater than 2MB. Append the letter k or K to indicate kilobytes, or m or M to indicate megabytes. The default value is chosen at runtime based on system configuration. For more information, see HotSpot Ergonomics
Examples:

-Xmx83886080
-Xmx81920k
-Xmx80m
Entnommen hier: java - the Java application launcher
Die Argumente müssen beim Start des Java-Programms übergeben werden, also bspw, so:
Code:
java -jar -xms16m -xmx32m someProgram.jar

Andere Informationsseite:
Speicher Tuning mit den JVM Parametern
 
selbst wenn man es drastisch vereinfacht läuft es bei mir auf einen OutOfMemory Exception raus:

Java:
import java.util.HashMap;
import java.util.Map;

public class Test {
	public static void main(String[] args) {
		Wald wald = new Wald();
		Blatt.printCounter();
	}
}

class Wald {
	private Map<Integer, Baum> wald;
	Wald () {
		wald = new HashMap<Integer, Baum>();
		for (int i = 0; i < 1000; i++) {
			wald.put(i, new Baum());
		}
	}
}
class Baum {
	private Map<Integer, Ast> baum;
	Baum () {
		baum = new HashMap<Integer, Ast>();
		for (int i = 0; i < 500; i++) {
			baum.put(i, new Ast());
		}
	}
}
class Ast {
	private Map<Integer, Zweig> ast;
	Ast () {
		ast = new HashMap<Integer, Zweig>();
		for (int i = 0; i < 100; i++) {
			ast.put(i, new Zweig());
		}
	}
}
class Zweig {
	private Map<Integer, Blatt> zweig;
	Zweig () {
		zweig = new HashMap<Integer, Blatt>();
		for (int i = 0; i < 10; i++) {
			zweig.put(i, new Blatt());
		}
	}
}
class Blatt {
	private Map<Integer, String> blatt;
	private static long counter;
	Blatt () {
		counter++;
		blatt = new HashMap<Integer, String>();
		for (int i = 0; i < 10; i++) {
			blatt.put(i, String.valueOf(i));
		}
	}
	static void printCounter() {
		System.out.println(counter);
	}
}

mal ne andere Frage: wie packst du in eine Liste einen key zusammen mit einem value rein?

edit: ok der exception zu urteilen, nimmst du eine map
 
naja du hast rund 500.000.000 HashMaps, je Integer/Object(Baum, Wald etc.). Wenn Du die in der DB ablegst dann viel Spaß 😉 Vielleicht solltet ihr Eure Strukturen überarbeiten ...
 
Hallo Gastredner,

dies ist leider nicht möglich, ich werde mit meinen 256MB auskommen müssen. Ich denke, dass ich hier eher an der Wurzel anfangen sollte, als den möglich-reservierbaren Speicher hochsetzen.

Hallo nrg,

wie du schon sagst, ist es ein sehr einfaches Beispiel. Bei mir kommen noch ResultSets, Statements etc. hinzu, wie auch der Profiler aufzeigt.

edit: ok der exception zu urteilen, nimmst du eine map

Ich benutze eine ArrayList und als Template-Klasse den Integer, welcher wiederum meine Keys repräsentiert.

Hier ein kleines bisschen Preudo-Code zu der Thematik:
Code:
ArrayList<Integer> baeume;
ArrayList<Integer> aeste;
ArrayList<Integer> zweige;
ArrayList<Integer> blaetter;

ResultSet rs;

rs = Connect.executeQuery("SELECT baeume FROM waelder WHERE wald = 123");
while (rs.next())
{
baeume.add(rs.getInt(1));
}

for (int i = 0; i  < baeume.size(); i++)
{
rs = Connect.executeQuery("SELECT aeste FROM baeume WHERE baum = " + baeume.get(i));
while (rs.next())
{
aeste.add(rs.getInt(1));
}
}

usw.

Wie man sieht, kommt so sehr schnell einiges zusammen.

Der Zwischenschritt mit der Abspeicherung der Datensätze in eine Liste ist leider nötig. Ich könnte ebenso gut die weitere Abhandlung direkt in die while-Schleife stecken. Nur geht dies leider nicht, da ich Datensätze von unten nach oben löschen möchte. Ich wandere also nachher durch meine Listen und lösche alle Blätter, dann die Zweige usw. So gewähre ich stets die Integrität meiner Daten. Würde ich von oben nach unten löschen, also zuerst die Bäume etc., dann hätte ich unter Umständen ein paar Leichen, nämlich die Äste, Zweige und Blätter, wenn das Löschen abbricht.

Ich hoffe, dass nun alles etwas klarer ist: Ich möchte meine Referenzielle Integrität gewährt haben, ohne auf Hilfmittel der Datenbank zurück zu greifen.



LG KK
 
Hallo Gastredner,

dies ist leider nicht möglich, ich werde mit meinen 256MB auskommen müssen. Ich denke, dass ich hier eher an der Wurzel anfangen sollte, als den möglich-reservierbaren Speicher hochsetzen.
Wenn ich das Bild aus dem Profiler überfliege, dann komme ich auch nicht ganz 16MB an Speicher, den du momentan verbauchst - weit weg von den 256MB, von denen du redest.
Bist du sicher, dass deiner VM diese 256MB tatsächlich bereits als maximaler Heap zugewiesen sind oder wurde dir einfach nur gesagt, dass dir 256MB Speicher zur Verfügung stehen?
 
Hallo Gastredner,

Wenn ich das Bild aus dem Profiler überfliege, dann komme ich auch nicht ganz 16MB an Speicher, den du momentan verbauchst - weit weg von den 256MB, von denen du redest.

Genau das machte mich auch stutzig!! Darum habe ich auch gefragt

Das Objekt java.util.HashMap$Entry[] ist laut Profiling "bloß" ~4MB groß, der Rest nicht viel größer; wieso läuft mir also der Heap voll?

Ich sage der IDE (Eclipse) mit dem Argument -Xmx256M, dass ich 256MB nutzen möchte, darauf sollte / müsste ich mich also verlassen.

Hallo fastjack,

Vielleicht sind in der DB schon mehr Daten vorhanden, als angenommen ?

Die Datensätze kann ich exakt zählen. Die Zahlen von 500 und 1000 kommen da also nicht von irgendwo.

Vielleicht solltet ihr Eure Strukturen überarbeiten ...

Das steht wirklich ausser Frage, werde ich auch machen!

Nur stehen dennoch die o.b. Fragen im Raum, die ich einfach nicht nachvollziehen kann.



LG KK
 
Mit welchen Parametern du Eclipse startest, ist Programmen, die aus der IDE heraus gestartet werden herzlich egal. Setz die entsprechenden vmargs in der run configuration.
 

Neue Themen


Zurück
Oben