# Problemlösungvorschläge zum beliebten Java Fehler : java.lang.OutOfMemoryError:



## IzYaBoi (22. September 2008)

Gute Morgen liebe Tutorialgemeinde,

ich möchte im folgenden Beitrag kurz auf meine Projekt eingehen an dem ich programmiere und was für Fehler dabei enstehen. Würde mich über Lösungen oder Ansatzhilfen freuen.

Zunächst möchte ich erwähnen das ich Server+CLient+Datenbank(MYSQL 5.0.22 ) Seitig  arbeite also nach dem 3 Schicht Modell mit dem Jboss Server 1.4.0.2. Soviel sei nur dazu gesagt. Sollte eigentlich unrelevant zum folgenden Fehler sein:

Das Tool an dem ich arbeit ist ein Exporttool das anhand von gesetzter Filter eine Textdatei erstellen soll mit fest definierter Satzlänge. Soweit so gut, auf meiner CLientseite gibt es folgende Methode die Aufgerufen wird wenn der benutzer seine Filter gesetzt hat und auf erstellen geklickt hat:


```
public void erstelleAusgabeDatei() {
		
		try {
			//Erstellung eines FileWriters und BufferedWriter                        
                       ausgabeWriter = new FileWriter(ausgabeDatei); 
			ausgabeBuff = new BufferedWriter(ausgabeWriter);
			
		      // "daten" vom Datentyp ARRAY ist eine Instanz meine DTO Objekts
                     // instanceMzaw10Facade.Holen_MZAW10_Export ist eine Servermethode die anhand folgender Parameter die gewünschte Menge holt

                      daten = instanceMzaw10Facade.Holen_MZAW10_Export(
					this.monathaelftenvon, this.monathaelftenbis,
					this.IDErhebung, this.Aland);
	//REST SELBSTERKLÄREND	
                 AnzahlDatensaetze = daten.length;
			
                       if (daten != null) {	

				for (int i = 0; i < daten.length; i++) {

					String EF10  = daten[i].getB_ef10();
					
					if (EF10.length() < 15) {
						int differenz = 15 - EF10.length();
						for (int j = 0; j < differenz; j++) {
							EF10 += " ";
						}
					}
					
				....
					
                            //	Zum Schluss setze ich die Abgefragten Felder zu einen String  zusammen und schreibe diese in eine Datei.				

				        String datensatz = EF10 + usw ...
                                        ausgabeBuff.write(this.datensatz);
					ausgabeBuff.newLine();
				}

			} else {
				JOptionPane.showMessageDialog(null, "Keine Sätze vorhanden");
			}
			ausgabeWriter.close();	
			ausgabeBuff.close();
		} catch (Exception e) {
			System.out.println(e + "Fehler beim Erstellen der Ausgabedatei");
		}
		
	}
```

Das Problem ist nun das ich diesen Fehler bei einer Anzahl >= 5000 bekomme : Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space 


Habe schon versucht den Heap Space mit -Xmx256 zu erhöhen aber das erscheint mir keine Dauerhafte Lösung bei so einer geringen Anzahl von Sätzen.  Zudem dies ne zeit Funktioniert hat bei ungefähr 13.000 Sätzen aber nun auch nix bringt. Zugriffzeit von minimalen 26 sek und max über 30 Oo was mir schon sehr viel erscheint.

Würde mich freuen wenn ihr irgendein Hinweis auf eine Lösung habt.


----------



## Thomas Darimont (22. September 2008)

Hallo,

ich würde an deiner Stelle erstmal versuchen nicht alles in einen riesen Buffer zu laden sondern alle Paar X Sätze ein "Häppchen" in die Ausgabe zu schreiben.

Weiterhin solltest du eher einen StringBuilder verwenden und Strings nicht per s+="" verknüpfen.

Ansonsten kann man dazu nicht viel sagen (nur dass eure Methodenbenennungskonventionen ziemlich Fragwürdig sind)

Gruß Tom


----------



## IzYaBoi (22. September 2008)

von den Methodenbenennungskonventionen wollen wir jetzt mal nicht sprechen da weiß is selbst das die nicht vernüftig sind aber die Serverseite programier ich nicht und da meine Firma alles in deutsch bennennt setze ich das nur so fort. =) aber das spiel keine Rolle.

ok die Strings mit nem stringbuilder per append zu verknüpfen werde ich mal machen.

aber was meinst du mit häppchen? du meinst ich soll mir aus meinen array nur ne bestimmte anzahl rausholen. diese mit write in die textdaetei schreibe diese mit close schließen und dann da ganze noch mal ausführen in einer schleife?


----------



## Andibert (22. September 2008)

Gemeint war:

Im Moment baust du dir einen riesen String und schreibst ihn dann in deinen Stream.
Stattdessen wäre es wahrscheinlich klüger die Strings, statt sie erst zu einem RIESIGEN zu verbinden direkt in der Schleife einzeln in den Stream zu schicken. Also Häppchenweise.
Das spart dir den riesen Alloc.


----------



## IzYaBoi (22. September 2008)

ich hab jetzt mal alles nochmal auskommentiert.


```
public void erstelleAusgabeDaei() {
		
		try {
			
			daten = instanceMzaw10Facade.Holen_MZAW10_Export(
					this.monathaelftenvon, this.monathaelftenbis,
					this.IDErhebung, this.Aland);
			
		System.out.println("test");
		}catch (Exception e) {
			System.out.println("Fehler beim Erstellen der Ausgabedatei" +e);
		}
	
	}
```

nix sinnvolles aber wollt nur mal schaun ob nun noch mal die Fehlermeldung kommt. und nun weiß ich echt nciht warum . bei mehr als 5295 datensätze in meinem array die ich durhc meine Holen methode aufd em server hole Oo siehe oben bekomm ich den fehler  wollte nur teste ob es über haupt beim erstellen noch zu ausgabe kommt ;/ was ist das los ? keine catch exception nur dieser java.lang.OutOfMemoryError: Java heap space fehler

kann es sein das es im zu viel ist wenn er diese result menge von 5295 ins Array kopiert Oo


----------



## Oliver Gierke (22. September 2008)

Der Clientcode nützt uns herzlich wenig. Das Memoryleak ist wohl in dieser kryptischen Methode (zu den nichtssagenden Namen hat Tom ja schon was gesagt). Wenn man sich nur halbwegs mit den Grundlagen von Java auskennt ist auch klar, dass ein catch (*Exception* ex) keinen OutOfMemory*Error* fangen kann. Was willst du auch machen, wenn kein Speicher mehr da ist? Es ist schlicht und einfach ein Bug .

Wenn der Code dieser obskuren Methode allerdings genauso ausschaut, wie der oben, werden sicherlich wenige Teilnehmer hier Lust haben sich das anzuschauen .

REINHAUN!


----------



## IzYaBoi (22. September 2008)

ich müsste nochmal klarstellen :


diese kryptischen Methode ist wie folgt auf der server seite in einer sessionbean geschrieben:
Der SQL String zum aufruf ist folgendermaßen deklariert.

String sqlexportmzaw10 = "{ call spmzExportMzaw10(?,?,?,?) }";


```
private void prepareprocexportmzaw10() throws SQLException
		{
			db.con = m_dataSource.getConnection();
			s_logger.log(Level.INFO,"about to execute" + sqlexportmzaw10);
			db.callstmt = db.con.prepareCall(sqlexportmzaw10);
			db.callstmt.setString(1,this.monhaelfte);
			db.callstmt.setString(2,this.monhaelftebis);
			db.callstmt.setInt(3,this.erhebung);
			db.callstmt.setString(4,this.land);
		}
```


```
/**
		 * Business method
		 * @ejb.interface-method  view-type = "remote"
		 */
	 
	 public MZAW10Export[] Holen_MZAW10_Export(String monathaelfte , String monathaelftebis,int erheb, String awbland){
	 	
	 	MZAW10Export[] result = null;
	 	
	 	this.monhaelfte = monathaelfte;
	 	this.monhaelftebis = monathaelftebis;
	 	this.erhebung = erheb;
	 	this.land = awbland;
	 	
		db = new DbResourceHolder();
	 	
		try {
	 		prepareprocexportmzaw10();
	 		db.res = db.callstmt.executeQuery();
	 		//Positionierung auf den letzten Satz und Abfrage der Zeilen
			db.res.last();
			int zeilen = db.res.getRow();
			if (zeilen > 0) {
			// Erneute Positionierung auf den ersten Satz
			db.res.first();
           result = new MZAW10Export[zeilen];         
			int i = 0;
			while (i < zeilen) {
				result[i] = new MZAW10Export();
				result[i].setB_ef1(db.res.getString("EF1"));
				result[i].setB_ef2(db.res.getString("EF2"));
				result[i].setB_ef3(db.res.getString("EF3"));
				result[i].setB_ef4(db.res.getString("EF4"));
				result[i].setB_ef5(db.res.getString("EF5"));
				result[i].setB_ef6(db.res.getString("EF6"));
				result[i].setB_ef7(db.res.getString("EF7"));
				result[i].setB_ef8(db.res.getString("EF8"));
				result[i].setB_ef10(db.res.getString("EF10"));
				result[i].setB_ef11(db.res.getString("EF11"));
				result[i].setB_ef12(db.res.getString("EF12"));
				result[i].setB_ef13u1(db.res.getString("EF13U1"));
				result[i].setB_ef13u2(db.res.getString("EF13U2"));
				result[i].setB_ef15(db.res.getString("EF15"));
				result[i].setB_ef30(db.res.getString("EF30"));
				result[i].setB_ef31(db.res.getString("EF31"));
				result[i].setB_ef32(db.res.getString("EF32"));
				result[i].setB_ef33(db.res.getString("EF33"));
				result[i].setB_ef34(db.res.getString("EF34"));
				result[i].setB_ef36(db.res.getString("EF36"));
				result[i].setB_ef40(db.res.getString("EF40"));
				result[i].setB_ef42(db.res.getString("EF42"));
				result[i].setB_ef50(db.res.getString("EF50"));
				result[i].setB_ef60(db.res.getString("EF60"));
				result[i].setB_ef65(db.res.getString("EF65"));
				result[i].setB_ef100(db.res.getString("EF100"));
				result[i].setB_ef101(db.res.getString("EF101"));
				result[i].setB_ef102(db.res.getString("EF102"));
				result[i].setB_ef103(db.res.getString("EF103"));
				result[i].setB_ef104(db.res.getString("EF104"));
				result[i].setB_ef105(db.res.getString("EF105"));
				result[i].setB_ef106(db.res.getString("EF106"));
				result[i].setB_ef107u1(db.res.getString("EF107U1"));
				result[i].setB_ef107u2(db.res.getString("EF107U2"));
				result[i].setB_ef107u3(db.res.getString("EF107U3"));
				result[i].setB_ef108(db.res.getString("EF108"));
				result[i].setB_ef109(db.res.getString("EF109"));
				result[i].setB_ef110(db.res.getString("EF110"));
				result[i].setB_ef111(db.res.getString("EF111"));
				result[i].setB_ef112(db.res.getString("EF112"));
				result[i].setB_ef113(db.res.getString("EF113"));
				result[i].setB_ef114(db.res.getString("EF114"));
				result[i].setB_ef115(db.res.getString("EF115"));
				result[i].setB_ef116(db.res.getString("EF116"));
				result[i].setB_ef117(db.res.getString("EF117"));
				result[i].setB_ef118(db.res.getString("EF118"));
				result[i].setB_ef119(db.res.getString("EF119"));
				result[i].setB_ef120(db.res.getString("EF120"));
				result[i].setB_ef121(db.res.getString("EF121"));
				result[i].setB_ef122(db.res.getString("EF122"));
				result[i].setB_ef123(db.res.getString("EF123"));
				result[i].setB_ef200(db.res.getString("EF200"));
				result[i].setB_ef201(db.res.getString("EF201"));
				result[i].setB_ef202(db.res.getString("EF202"));
				result[i].setB_ef203(db.res.getString("EF203"));
				result[i].setB_ef204(db.res.getString("EF204"));
				result[i].setB_ef205(db.res.getString("EF205"));
				result[i].setB_ef206(db.res.getString("EF206"));
				result[i].setB_ef207u1(db.res.getString("EF207U1"));
				result[i].setB_ef207u2(db.res.getString("EF207U2"));
				result[i].setB_ef207u3(db.res.getString("EF207U3"));
				result[i].setB_ef208(db.res.getString("EF208"));
				result[i].setB_ef209(db.res.getString("EF209"));
				result[i].setB_ef210(db.res.getString("EF210"));
				result[i].setB_ef211(db.res.getString("EF211"));
				result[i].setB_ef212(db.res.getString("EF212"));
				result[i].setB_ef213(db.res.getString("EF213"));
				result[i].setB_ef214(db.res.getString("EF214"));
				result[i].setB_ef215(db.res.getString("EF215"));
				result[i].setB_ef216(db.res.getString("EF216"));
				result[i].setB_ef217(db.res.getString("EF217"));
				result[i].setB_ef218(db.res.getString("EF218"));
				result[i].setB_ef219(db.res.getString("EF219"));
				result[i].setB_ef220(db.res.getString("EF220"));
				result[i].setB_ef221(db.res.getString("EF221"));
				result[i].setB_ef222(db.res.getString("EF222"));
				result[i].setB_ef223(db.res.getString("EF223"));
				result[i].setB_ef300(db.res.getString("EF300"));
				result[i].setB_ef301(db.res.getString("EF301"));
				result[i].setB_ef302(db.res.getString("EF302"));
				result[i].setB_ef303(db.res.getString("EF303"));
				result[i].setB_ef304(db.res.getString("EF304"));
				result[i].setB_ef305(db.res.getString("EF305"));
				result[i].setB_ef306(db.res.getString("EF306"));
				result[i].setB_ef307u1(db.res.getString("EF307U1"));
				result[i].setB_ef307u2(db.res.getString("EF307U2"));
				result[i].setB_ef307u3(db.res.getString("EF307U3"));
				result[i].setB_ef308(db.res.getString("EF308"));
				result[i].setB_ef309(db.res.getString("EF309"));
				result[i].setB_ef310(db.res.getString("EF310"));
				result[i].setB_ef311(db.res.getString("EF311"));
				result[i].setB_ef312(db.res.getString("EF312"));
				result[i].setB_ef313(db.res.getString("EF313"));
				result[i].setB_ef314(db.res.getString("EF314"));
				result[i].setB_ef315(db.res.getString("EF315"));
				result[i].setB_ef316(db.res.getString("EF316"));
				result[i].setB_ef317(db.res.getString("EF317"));
				result[i].setB_ef318(db.res.getString("EF118"));
				result[i].setB_ef319(db.res.getString("EF319"));
				result[i].setB_ef320(db.res.getString("EF320"));
				result[i].setB_ef321(db.res.getString("EF321"));
				result[i].setB_ef322(db.res.getString("EF322"));
				result[i].setB_ef323(db.res.getString("EF323"));
				result[i].setB_ef400(db.res.getString("EF400"));
				result[i].setB_ef401(db.res.getString("EF401"));
				result[i].setB_ef402(db.res.getString("EF402"));
				result[i].setB_ef403(db.res.getString("EF403"));
				result[i].setB_ef404(db.res.getString("EF404"));
				result[i].setB_ef405(db.res.getString("EF405"));
				result[i].setB_ef406(db.res.getString("EF406"));
				result[i].setB_ef407u1(db.res.getString("EF407U1"));
				result[i].setB_ef407u2(db.res.getString("EF107U2"));
				result[i].setB_ef407u3(db.res.getString("EF107U3"));
				result[i].setB_ef408(db.res.getString("EF408"));
				result[i].setB_ef409(db.res.getString("EF409"));
				result[i].setB_ef410(db.res.getString("EF410"));
				result[i].setB_ef411(db.res.getString("EF411"));
				result[i].setB_ef412(db.res.getString("EF412"));
				result[i].setB_ef413(db.res.getString("EF413"));
				result[i].setB_ef414(db.res.getString("EF414"));
				result[i].setB_ef415(db.res.getString("EF415"));
				result[i].setB_ef416(db.res.getString("EF416"));
				result[i].setB_ef417(db.res.getString("EF417"));
				result[i].setB_ef418(db.res.getString("EF418"));
				result[i].setB_ef419(db.res.getString("EF419"));
				result[i].setB_ef420(db.res.getString("EF420"));
				result[i].setB_ef421(db.res.getString("EF421"));
				result[i].setB_ef422(db.res.getString("EF422"));
				result[i].setB_ef423(db.res.getString("EF423"));
				db.res.next();
				i++;
			}
			s_logger.log(Level.INFO, "select all ok." + i);
			} 
			else  // keine Zeile gefunden
			{result = null;}; 
		} catch (SQLException ex) {
			s_logger.log(Level.INFO, "SQLException" + ex);
			result = null;
		    throw new EJBException(ex); 
		} catch (NullPointerException e) {
			s_logger.log(Level.INFO, "NullPointerException");
			result = null; // kein resultset
		}finally{
			db.close();
		}
	
	 	return result;
	 	
	 }
```

Ich habe mir in einer gespeicherten Prozedure anhand der gesetzen paramter eine tabelle gefüllt die genau den Aufbau der Datei haben soll. diese sogenannte temp tabelle würd mit den gewünschten feldern aus andern tabellen gefüllt.
aber ich glaub das sollte ncith groß das problem sein ich habe mein statement ausgeführt mit angabe der parameter. Und bekomme auch mengen von 13.000  sätzen in 7,2 sek .
mein statment kann gewiss noch optimiert werden . aber ich bekomm erst mal das gewünscht resultset.

was sol ich eurer meinung nach tun?


----------



## IzYaBoi (22. September 2008)

also ich hab mal grade meine server seite getestet scheint echt an der server seite zu liegen  Mein JUnit test zu dieser holen methode schmeist mir auch ein Outofbounderror : java heap space

wie kann ich jetzt am sinnvollsten mein result füllen damit ich kein fehler bekomm? ab einer kleinen menge von datensätzen ging das ja nooch gut aber anscheinend gibt es aber bei mehr datensätzen  echt ein memory leak wenn ich das auf diese weiße mache ! 


kan mir jemand helfen wäre toll


----------



## Andibert (22. September 2008)

Auf den ersten Blick würde ich sagen, kannst du es vergessen deine Ergebnisse in EINEM Array vom Server auf den Client zu holen.
Das heißt du musst die Serverseite ändern und dir immer handliche Stücke holen. Also dir vom Result die ersten z.B.100 Zeilen geben lassen, die zu deinem Clienten übertragen und behandeln, und dann die nächsten 100 usw.

Leider habe ich nicht so viel Ahnung davon wie man Clien-Server programmiert, wie davon wie man die VM in die Knie zwingt, daher muss dir mit der gestaffelten Datenübertragung jemand anderes helfen.

Das EINE Array mit ALLEN Ergebnissen wird in deinem Falle einfach nicht machbar sein... die Daten sind wohl nicht umsonst in ner Datenbank gespeichert


----------



## IzYaBoi (23. September 2008)

und wie stelle ich das am besten an? ist das überhaupt der richtige Ansatze? Die Leute die schon Serveranwendungen programmiert haben sind nun  gefragt!!

freue mich auf vorschläge tips oder weitere ideen..


----------



## tim staeglich (23. September 2008)

Hi,

bei Client-/Serveranwendungen (wie überall) ist die Performance sehr wichtig. Daher würde ich meinen Vorrednern beipflichten - unterteile Deine Ergebnisse in Häppchen.
Bei Strings z.B. kann man im Extremfall byte weise übertragen.

Noch ein anderes Problem: Selbst ein gutes DBMS ist nicht schnell, wenn man ein select macht, bei welchem  n-tausend Zeilen herauskommen. Deshalb hier ein Denkansatz mit etwas Pseudocode. 
Du solltest die Ergebnisspalten von vornherein mit einem "limit" SQL Befehl begrenzen. Oder/und: Du könntest die Abfrage nach IDs in Intervalle unterteilen (where id between startIndex and endIndex).
Dann iterierst Du die Indexe hoch, z.B.:

- startIndex=0, endIndex =100
- Abfrage 
- wenn Ergebnis wirklich 99 Zeilen umfasst, zähle hoch
- startIndex = 101, endIndex = 200
- Abfrage
- usw...

Zusätzlich kannst Du probieren, 
Jedes Ergebnis ResultSet zeilenweise zu senden/empfangen.
Das Senden initiierst Du, indem Du einen Buffer (z.B. ResulSet Array) einrichtest,
der beim Senden abgearbeitet wird. D.h. für jede erfolgreiche Abfrage fügst Du einem (z.B.) ArrayList Buffer dieses ResultSet hinzu. Dann iterierst Du die BufferList durch,
gibst die Ergebnisse in dem jeweiligen ResultSet aus

Noch ein Tipp: Check mal Dein Programm auf Rekursionen.
Selbst bei noch so vielen Array Einträgen, ist ein OOME unwahrscheinlich.
In Deinem Code gibt es Möglichkeiten für Rekursionen in Methoden, allerdings kann ich die ja nicht einsehen/nachprüfen.
Ruft Dein Programm sich irgendwo irgendwie selbst auf?

VG Tim


----------



## Andibert (24. September 2008)

Der out of Memory ist hier gar nicht so unwahrscheinlich, auch ohne Rekursion.
Ein Result Objekt hat immerhin 100 Strings (geschätzt) bei 10 k Ergebnissen kommt man auf 1Mio Strings. lass die jeweils im Schnitt 128Byte groß sein, und schon Kommst du auf einen Speicherbedarf von 128MByte was dem schon beträchtlich nah kommen sollte, was der Server einem einzelnen Prozess zugestehen dürfte.

Die vorgeschlagene Lösung fürs portionieren scheint mir der beste weg. da so keine Änderungen an der Serverseite nötig werden.


----------



## IzYaBoi (24. September 2008)

rekursion schließe ich aus aber bin mittlerweile an einer andern Lösung ich sag euch dann wenn ich fertig bin was ich gemacht habe


----------

