# java.lang.OutOfMemoryError: Java heap space



## Fry (9. Okt 2005)

Hallo,


```
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
```

Was genau bedeuted das? Also klar der Speicher reicht nicht hin. Ich bearbeite da ein Array mit gut 80 Spalten und 5000 Zeilen. Wie kann ich das messen ob der Speicher reicht etc? Ich kann mir auch nicht ganz vorstellen, dass 512MB RAM nicht reichen, und ich weiß auch nicht wofür "heap space" steht.

Wäre für Hilfe dankbar

Fry


----------



## Beni (9. Okt 2005)

Ein Java Programm benutzt (leider) nur einen kleinen Teil des RAMs, bei etwa 64 MB ist normalerweise Schluss.

Du kannst das Programm mit dem Zusatz "-Xmx512m" starten, wenn es maximal 512 MB RAM benutzen darf.

Der Heapspace ist der Platz im Ram, in dem all die Objekte abgelegt werden, welche an verschiedenen Stellen benutzt werden.


----------



## Fry (9. Okt 2005)

Hallo Beni,

danke für die Erläuterung. Ich frage nochmal anders herum, ich habe praktisch eine Schleife im Programm, die 6 mal das gleiche macht: immer Daten laden in ein Array packen und dann analysieren. 

1) Dieses Problem müßte dann ja bei vielen vorkommen, denn sooooo riesig sind die Datenmengen ja auch nicht oder? Ich meine ein Array 89*5000 ist nicht klein, aber das kann ja nicht Java sprengen oder?

2) Gibts es da noch andere Möglichkeiten? ich denke gerade daran, ob es Sinn machen würde den Garbage Collector extra irgendwie anzuwerfen? Obwohl eingentlich macht er das ja auch ganz ordentlich...

Ich probiere deinen Tipp mal aus!

EDIT: also selbst mit 512mb (ich übergebe das Argument in Eclipse) geht das nicht. Ist für mich jetzt schwer vorstellbar, dass es net geht. Was kann man da sonst noch machen bzw. überprüfen?

Danke
Fry


----------



## Beni (10. Okt 2005)

Was steht denn im Array? Was für Objekte sind das?
Garbage Collector anwerfen nützt nichts, der kommt automatisch. Du könntest mal überlegen ob du deinen Algorithmus so umschreiben kannst, dass du keine Arrays mehr benötigts.


----------



## Bleiglanz (10. Okt 2005)

>>immer Daten laden in ein Array packen und dann analysieren.

machst du jedesmal ein neues Array (oder doch lieber das vorhandene wiederverwenden)? denn dann wären wir ja schon bei
6 x 89 x 5000, also ungefähr 2,7 Millionen ArrayEinträge: stellt sich die Frage von welchem Typ die Einträge sind...


----------



## Fry (10. Okt 2005)

Hallo,

@Beni: In meinem Array stehen nur Strings drin, die werden aus einer Tabdatei eingelesen.
@Bleiglanz: Du hast Recht, es wird jedesmal ein neues Array erzeugt. Ich habe nur erwartet - weil nach dem Durchlauf nichts mehr auf das Array verweist - dass es automatisch dann per Garbage Collector entfernt wird.
Ablauf ist wie folgt:

- Datei einlesen --> Array draus machen --> berechnen ---> Werte schreiben
- neue Datei einlesen --> neues Array machen (kann unterschiedlich groß sein) --> berechnen --> Werte schreiben
- ... und das sechs mal

Es ist also nie das gleiche Array, da es nach der Analyse auch wieder in eine Datei geschrieben wird, dürfte da doch keine Referenz mehr drauf sein und müßte "entsorgt" werden, oder?

Ein Array finde ich da praktisch, damit habe ich eine Matrix und kann bequem mit rechnen. Was soll man sonst nehmen? ArrayList ist doch intern auch nur Array und geht doch bestimmt noch mehr in Speicher, oder?

Fry


----------



## Beni (10. Okt 2005)

Also über den linken Daumen gerechnet, komme ich auf ein Programm das nur so um die 20-100 MB benötigt. ???:L 

Was rechnest du denn da? Darf man den Code mal sehen?  :### 

Du kannst es mal mit einer anderen Datenstruktur, wie einer LinkedList, versuchen.  Aber das benötigt eher noch zusätzlichen Speicher...


----------



## Bleiglanz (10. Okt 2005)

und was ist "berechnen" und "Werte schreiben"

na ja, wenn jeder String nur 5 Zeichen hat, dann sind das schon 27MB

trotzdem komisch, dass die GC solch kurzlebigen Strings nicht sofort verwirft - speicherst du vielleicht irgendwo Referenzen auf das Array?


----------



## Fry (10. Okt 2005)

Hallo ihr zwei,

also ich speicher mein Array mit einer setMethode. Aber beim nächsten Durchlauf wird das wieder überschrieben. Also ich habe eine Variable:


```
private String[][] alleDaten;
```

und mit der get und set Methode hole oder schreibe ich das

```
setAlleDaten(String[][] array)
{
alleDaten = array;
}
```
get entsprechend.

Also 5 Ziechen haben die immer! Es sind auch ein paar Werte die weniger haben, aber auch wiederum viele die mehr haben. Ich Durchschnitt liege ich über 5 Zeichen.

Was an Code soll ich mal rausstellen? In Summe ist das schon ziemlich viel.

Ich habe eine "Oberklasse" die die ganzen Analysemethoden bereitstellt. Dann erstelle ich eine gewisse Anzahl von Objekten (je nachdem was ich betrachten will), jedes ist von der Oberklasse abgeleitet. Die nudelt das Programm dann hintereinander ab.

Fry


----------



## Bleiglanz (10. Okt 2005)

normalerweise sollte bei 

alleDaten = array; 

als alte Array "löschbar" werden, hast du wirklich nirgendwo anders eine Referenz darauf?

(irgendwo kommt ja das übergebene Argument array her?)

ach ja: wenn du dein tabfile liest, dann liest du das hoffentlich nicht komplett ein sondern zeilenweise?

und machst dann array[zeilenindex] = gelesenerString.split("\t") oder sowas


----------



## Fry (11. Okt 2005)

Hallo Bleiglanz,

1) Ja ich lese das zeilenweise mit BufferedReader (readline()) und StringTokenizer ein

2) Kann man schnell feststellen ob noch irgendeine Referenz auf das Objekt ist? Das sind in Summe knappe 4000 Zeilen Code, wo ich mich dann durchklicken müßte...bleibt aber nix anderes über oder?
Nur aber zum richtigen Verständis, wie würde konkret eine Referenz aussehen? Oder was wäre typisch, dann könnte ich direkt mal suchen. Irgendein Automatismus wäre cool   

Ich versuche aber das schonmal darszustellen:

Also Array wird gelesen und dann weiß ich die Größe also:

```
alleDaten = new String[getAnzahlDerZeilen()][getAnzahlDerSpalten()];
```

danach lese ich die Datei dann wirklich und jetzt passt sie ja auch ins Array genau rein:

```
BufferedReader reader = new BufferedReader(new InputStreamReader(downloadUrl.openStream()));
						
			boolean done = false;
			reihenZähler = 0;
            
			while(!done)
			{
				String line = reader.readLine();
				if(line == null)
					done = true;
				else
				{
					//Die Zeile vom Lesen aufsplitten...
					String[] tempArray = line.split("\\t");
				
					//...und in temporäres Array füllen
					for(spaltenZähler = 0; spaltenZähler < getAnzahlDerSpalten(); spaltenZähler++)
					{
						alleDaten[reihenZähler][spaltenZähler] = tempArray[spaltenZähler];
					}
					
					reihenZähler++;
					
					
				}
				
			}
                        setAlleDaten(alleDaten);
			reader.close();
```

Das steht halt in einer Klasse drin, wovon ich ein Objekt erzeuge wenn der erste Durchlauf startet. Wenn dann der nächste Durchlauf startet, greife ich auf die Daten vom ersten Durchlauf gar nicht mehr zurück, denn die werden am Ende des Durchgangs ja auch geschrieben. Beim zweiten Durchlauf wird von der Klasse dann ein neues Objekt erzeugt und so weiter...

Dankeschöne und gute Nacht
Fry


----------



## Bleiglanz (11. Okt 2005)

ersetz erstmal den StringTokenizer durch String#split, so wie es in deinem zweiten Codefragmet steht??

und

```
alleDaten = new String[getAnzahlDerZeilen()][getAnzahlDerSpalten()];
```
das ist komisch: woher weisst du vor dem einlesen, wieviele Zeilen in der Datei drin sind? liest du zweimal?

mach lieber eine ArrayList<String[]>, dann brauchst du vorher nicht wissen wieviele Zeilen kommen


interessant ist 

```
alleDaten = new String[getAnzahlDerZeilen()][getAnzahlDerSpalten()]; 

// dann befüllt

setAlleDaten(alleDaten);
reader.close(); 

// was kommt danach? das methodenende? 
// ist "alleDaten" einen lokale Variable
```


----------



## Fry (11. Okt 2005)

Moin,

1) Ja ich lese zweimal, einmal um Arraygröße zu bestimmen, zweites mal um das einzutüten. Hab das mit der ArrayList nie so richtig hinbekommen, die muss dann ja noch zweidimensional sein. Verbracuht das aber erst Recht nicht noch mehr Speicher?

2) also alleDaten ist für die Klasse global, also für alle Methoden aus der Klasse sichtbar. Danach ist das Methodenende und es kommt nix mehr

Fry


----------



## Bleiglanz (11. Okt 2005)

1) Nein, spar dir das zweimal lesen  Nimm einfach eine ArrayList<String[]>, wobei du beim lesen immer add(line.split("\\t")); aufrufst

2) Ist schonmal schlecht: da alleDaten eine Membervariable ist, bleibt nach dem Aufruf von setAlleDaten(alleDaten) ja diese Referenz auf das Array noch übrig

Wenn du beim zweiten Durchlauf "greife ich auf die Daten vom ersten Durchlauf gar nicht mehr zurück" dann eine neue Klasse erzeugst, und die erste noch lebt, dann kann das Array nicht entsorgt werden

Obwohl das Design IMHO schon ziemlich kaputt ist, könnte kurzfristig eventuell ein

```
setAlleDaten(this.alleDaten);
reader.close(); 
this.alleDaten = null; // ausnullen ist meistens ein schlechter Rat
```
helfen


----------



## Bleiglanz (11. Okt 2005)

Tipp

```
public static String[][] getArrayFromTabFile(URL downloadUrl)
            throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(
                downloadUrl.openStream()));
        String line = null;
        List<String[]> zeilenListe = new ArrayList<String[]>();
        while (null != (line = reader.readLine())) {
            zeilenListe.add(line.split("\\t"));
        }
        reader.close();
        return zeilenListe.toArray(new String[zeilenListe.size()][]);
    }
```


----------



## Fry (11. Okt 2005)

Hallo Bleiglanz,

zunächst dochmal nen dickes danke, dass du so lange dabei bleibst - Thx 

erste Version hab ich probiert und bekomme NullPointerException .... also doch noch irgendwo ne Referenz drauf.

Die zweite Version kann ich doch gefahrlos einsetzen oder? Durch static muss ja jedes Objekt die gleiche Variable nehmen, dadurch sollte das doch weg sein oder?

Mhhhh hätt ich ja auch mal drauf kommen können  :roll: Ich probiers nachher mal gleich aus. Noch eine Frage, da ich ja beim Analysieren dauernd auf das Array zugreife kann ich doch aber dann mit get und set drauf zugreifen oder? Denn würde das das so nehmen wie du geschrieben hast, dann würde er ja jedesmal das Teil runterladen. Das soll ja nur einmal geschehen.

Thx
Fry


----------



## Bleiglanz (11. Okt 2005)

> Durch static muss ja jedes Objekt die gleiche Variable nehmen, dadurch sollte das doch weg sein oder?


ja, die Methode sollte (weil static UND weil keine static Variablen beteiligt sind) tatsächlich sauber sein



> Noch eine Frage, da ich ja beim Analysieren dauernd auf das Array zugreife


warum?

schreib eine Methode


```
analyse(String[][] arr){
// arr hat Block-Level-Scope
}
```
die du so aufrufst

```
analyse(getArrayFromTabFile(...));
```

und spar dir das Speichern des String[][] in irgendwelchen Membervariablen


----------



## Fry (11. Okt 2005)

Hi,

also ich bin gerade am umbaun ;-)
Stellt sich aber noch folgendes Problem mit der toArray() Methode der ArrayList...da bekomme ich kein 2D Array raus, das ist immer nur einfach gefüllt. Ich finde in der Doku auch nur zwei Methoden aber beide geben nur ein 1D Array zurück  :roll: 
Wie krieg ich Array[][] hin?

Fry

STOPP: Geht irgendwie doch, muss erstmal weiter überprüfen


----------



## Fry (11. Okt 2005)

So,

hier nochmal Zusammenfassung:

- ich habe jetzt das ganze Programm nochmal zerpflügt und neu strukturiert (sagen wirs so, alles rausgeworfen und nur noch eine Methode zum Testen übergelassen :lol: )
- ABer: Es tritt immer noch auf. Allerdings habe ich jetzt nochmal die Quelle analysiert. Meine Annahme am anfang war falsch, ich habe im Download mehrere Textfelder, in denen als durchaus einige Zeichen enthalten sein können...mehr als 5 im Durchschnitt auf jeden Fall.
- Eine blöde Frage bleibt aber dennoch....wieso geht das in Excel :roll: da kann ich genau das gleiche kopieren und dann Auswertungen drüberlaufen lassen...und es tut.
- Es ist so ziemlich bei 5000 Zeilen Ende, davor geht alles

EDIT: Ich habe jetzt nochmal etwas rumprobiert und eine interessante Entdeckung gemacht. Ich hab unter gewissen Voraussetzungen möglichkeiten die tab Datei zu beeinflussen. So habe ich das mal getan und jetzt nur noch 12 Spalten.
- die ersten 6 Spaten haben max 14 Zeichen
- die 7. Spalte immer 5
- die 8. max 18 meißt aber weniger
- die 9. und 10. jeweils genau 8
- die 11. nicht mehr als 15
- die 12. auch nicht mehr als 15

Ich habe das mit dem einlesen jetzt so gemacht wie du gesagt hast, und auch im nachhinein probiert nochmal das array = null zusetzen. Hilft aber auch nichts, er ballert immer wieder mit heap space raus. Dieses aber genau immer dann, wenn die Anzahl der Zeilen gegen 5000 geht. Vorher keine Probs.

Noch ne Idee? :roll:

Gute Nacht ;-)

Fry


----------



## Bleiglanz (12. Okt 2005)

startest du nach wie vor mit -Xmx512 oder?

12 Spalten 
5000 Zeilen
100 Zeichen pro String (grosszügig) = 200 Bytes
6 Wiederholungen

ca. 72 MB

daran KANN es nicht liegen, wahrscheinlich liegts an deinen Auswertungen. Brauchen die auch viel Speicherplatz??


----------



## Fry (12. Okt 2005)

Huhu 

Also ich gebe diesen Parameter bei Eclipse mit. Bei "Run..." gehe ich dann auf Arguments und trage dort "-Xmx512m" ein.
Ich habe aber noch was herausgefunden. Ich ändere die Einlesedatei (unter gewissen Voraussetzungen kann ich das machen ohne das es das Ergebnis ändert) und dann habe ich zwischen 100 und 700 Zeilen. Und das Programm läuft anstandlos durch. Die Frage ist nur ob sich das irgendwie "anstaut" und dann irgendwann einem wieder um die Ohren fliegt. Ich habe komplett alle Auswertungen herausgenommen und nur noch folgendes drin (nur pseudohaft, da Code auf anderem Rechner...)

```
public void testMethod(int id, String[][] allDataArray)

GregorianCalendar cToday = new GregorianCalendar();
GregorianCalendar cCheck = new GregorianCalendar();

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

calendar.add(Calendar.MONTH, 1);

ArrayList list = new ArrayList();
list.add(new String(sdf.format(cToday.getTime());

for (int i=1; i<allDataArray.length; i++) // ote zeile nicht betrachten
{
....
if(.....) //hier kommt eine IF Abfrage, die prüft ob in vier Spalten Bedingungen erfüllt sind
{
//Einige Zellen sind Daten, die werden geparst und in den Calender geschrieben
int tag = Integer.parseInt(<entsprechnedeZeile>.toString().substring(0,2)//In Klammer ein bestimmter Wert in einer Spalte wird zerschnippelt und der tag extrahiert
int monat = ...
int jahr = ...
cCheck.set(jahr,monat,tag);

if(cToday.compareTo(cCheck) !=0)
{
//Hier ein paar Prüfungen um was für einen Tag es sich handelt
}

list.add(new String(allDataArray[i][SPALTE]
list.add(new Integer(allDataArray[i][ANDERE_SPALTE]
... (insgesammt häng ich vier Werte pro Durchlauf an die ArrayListe, die ist für gewöhnlich keine 20 Elemente lang)


//Erzeugen einer klasse die das Schreiben vornimmt
objektDerKlasse.schreibe(....);
```

Wenn ich also die Berechnung machen lasse mit dieser einen Methode und ich habe an die 5000 Zeilen ---> Speicherprob  :gaen: 
Wenn ich die Berechnung mit vorher eingeschränkter Datei machen ---> läuft durch 

Ich glaube auch nicht so Recht dran, dass es nicht wegen des Speichers geht, aber gibt es eine Möglichkeit das herauszufinden wieviel Speicher welches Objekt belegt? Das wäre cool 


Thx

Fry


----------



## Bleiglanz (12. Okt 2005)

richtigen Profiler verwenden? oder

java -verbose:gc

oder

-Xrunhprof[:help][:<suboption>=<value>,...]
    Enables cpu, heap, or monitor profiling. This option is typically followed by a list of comma-separated "<suboption>=<value>" pairs. Run the command java -Xrunhprof:help to obtain a list of suboptions and their default values.

schau mal in die tooldoc


----------



## Fry (12. Okt 2005)

Hallo Bleiglanz,

hier nochmal ein richtig dickes Danke an dich. :applaus:
Ich probiere jetzt mit den Einstellungen nochmal herum, und sollte das mit dem heap speicher nochmal auftreten, kann ich das jetzt ein wenig analysieren  :###   ???:L
Ich werde das Programm jetzt mit deinen Tipps nochmal neu aufbauen und schaun ob es dann klappt!

Ich setzt das jetzt mal auf gelöst, wenn konkrete Probleme gibt kann ich ja dann die Ausgaben der Konsole posten.

Thx nochmal 

Fry 

EDIT: Der Button mit dem Häckchen geht nicht?!


----------

