# JExcel  OutOfMemory bei ca. 25.000 Zeilen?



## Sebastian29 (7. April 2008)

Hi!

Für mich ist es eigentlich keine gute Idee, den Heap Speicher zu erhöhen -> java -Xmx512 usw., weil die Tabelle mit den sämtlichen Daten immer größer wird und dann später wieder den Heap Speicher erhöhen!

Ich möchte gerne alle 10000 Zeilen in die Excel-Datei also blockweise speichern!

Habt ihr eine Idee?

Gruß
Sebastian29


----------



## zerix (7. April 2008)

Hallo,

ich glaube nicht, dass es an den 10000 Zeilen liegt. Da ich aber dein Programm nicht kenne, kann ich dir leider sagen, wo dein Speicherleck ist.

MFG

Sascha


----------



## Sebastian29 (8. April 2008)

Hi Sascha!

Der Heapspeicher ist bis zu 512 MB groß und die Arrays für die Zeilen und Spalten ( z.B. 25.000 x 11 = 275.000, d.h. 275Kb Excel-Datei ) sind eigentlich gar nicht groß, aber es wäre besser, während des Exportierens blockweise in die Excel-Datei speichern! Im untenstehenden Code wird ja mit einem Schlag in die Excel-Datei gespeichert, was auch wirklich nicht gut ist.

Also, das ist die Methode, mit der ich die Daten zu Excel exportiere:


```
public static void exportToExcel( ...... )
	{
		JExcel.backcolor = backcolor;
		
		try 
		{
			workbook = Workbook.createWorkbook(new File( filename ));
			
			wSheet = workbook.createSheet( sheet, sheetTabIndex );
			wSheet.getSettings().setAutomaticFormulaCalculation(true);

			
			
			// Formatierung
			cellFormat = new WritableCellFormat();
			cellFormat.setAlignment( Alignment.CENTRE );
			
			
			
			
			// Spaltenüberschriften
			for( int column = 0; column < columnName.length; column++ )
			{
				wSheet.addCell(new Label(column, 0, String.valueOf( columnName[ column ].m_title ) ));
			}
			
			
			
			
			// Daten
			for( int row = 1; row <= tableModel.getRowCount(); row++ )
			{
				for( int column = 0; column < tableModel.getColumnCount(); column++ )
				{
					StringBuffer buf = new StringBuffer();
					buf.append( tableModel.getValueAt( (row-1), column) );

					if( cellFormat != null )
						label = new Label(column, row, buf.toString(), cellFormat );
						
					if( label != null )
						wSheet.addCell( label );
						
					buf = null;
				}
			}
			
			// Ende
			workbook.write();
			workbook.close();
			
			JOptionPane.showMessageDialog( parent, "Die Daten wurden erfolgreich nach Excel exportiert!", TEXTSCHALTER_EXCEL_EXPORT, JOptionPane.INFORMATION_MESSAGE );
		} 
		catch (IOException e) {
			JOptionPane.showMessageDialog( parent, TEXT_ZUGRIFF_VERWEIGERT, TEXTSCHALTER_EXCEL_EXPORT, JOptionPane.ERROR_MESSAGE );
		} 
		catch (RowsExceededException e) {
			e.printStackTrace();
		} 
		catch (WriteException e) {
			e.printStackTrace();
		}
	}
```


----------



## zerix (8. April 2008)

Ich hab mit JExcel leider noch nicht gearbeitet, deshalb weiß ich auch nicht so ganz wie es funktioniert. Ich schätze aber mal, dass es an deiner for-Schleife liegt, dass du eventuell einen OutOfMemoryError hast. 

Leg den StringBuffer mal vor der Schleife an und setz einfach nur die Länge wieder auf 0. Muss auch immer ein neues Label angelegt werden?

MFG

Sascha


----------



## Sebastian29 (8. April 2008)

StringBuffer habe ich soweit vor die Schleife gesetzt und die Länge auf 0 gesetzt! Die Klasse Label kommt von der API JExcel und in der Dokumentation steht, dass sie in der 2. Schleife stehen muss!

Im Debug habe ich mal genauer untersucht, wie JExcel überhaupt die sämtliche Daten sammelt und zwar werden die Spalten-Daten in den jeweiligen RowRecord-Array gespeichert! Das verbraucht wirklich viel zu viel Speicher und logischerweise kommt auch eine OutOfMemoryError.

1. Zeile: RowRecord[0][0] = Label(...)
            RowRecord[0][1] = Label(...)
            RowRecord[0][2] = Label(...)
            RowRecord[0][3] = Label(...)
            RowRecord[0][4] = Label(...)
usw. bis
25.000. Zeile: RowRecord[25000][0] = Label(...)
                     RowRecord[25000][1] = Label(...)
                     RowRecord[25000][2] = Label(...)
                     RowRecord[25000][3] = Label(...)

Ich weiss nicht, ob du die Klasse WeakHashMap kennst? Das wäre eine geniale Idee für mich, was mir eben eingefallen ist! Als Beispielcode:

Setzt mal bei der Zeile "System.out.println("25000");" einen Breakpoint und schau dir mal die Ausgabe an!

Das Programm läuft unendlich, da intern immer wieder aufgeräumt wird und die Objekte nicht länger verwendet werden. Die Map nimmt beispielweise 39 Elemente auf, davon werden dann 19 gelöscht und es sind nur noch 20 Elemente vorhanden und im nächsten Durchlauf werden noch mal weitere Objekte gelöscht. So stelle ich mir gerade für JExcel vor. Da kann der Speicherverbrauch nie überflüssig sein und es geht bestimmt auch viel schneller. Die Klasse "WeakHashMap" liegt in java.util.*; !


```
int counter = 0;
		WeakHashMap hm = new WeakHashMap();
		while( true )
		{
			if( counter == 25000 )
				System.out.println("25000");
			
			hm.put( new Long( counter ), "test" + counter );
			System.out.println( ( counter++ ) + ":" + hm.size() );
		}
```

Gruß
Sebastian29


----------



## zerix (8. April 2008)

Sicher kenne ich die Klasse. Die WeakHashMap arbeitet mit WaekReference. 
Ein großes Speicherleck kann sein, dass man Objecte in mehreren Listen speichert. Wenn man aber dann ein Object löschen möchte und vergisst es aus einer Liste zu löschen, bleibt es immer im Speicher, weil ja eine Reference auf das Object existiert. 

Bei WeakReferences ist das anders. Wenn du ein Object mit einer solchen Reference in einer Liste speicherst und es existiert keine weitere Reference mehr auf das Object (außer der WeakReference) dann wird das Object bei der nächsten GC aus dem Speicher entfernt.

Aber wie ich weiter oben schon erwähnt hab, ich kenne dein Programm nicht und weiß deshalb auch nicht wo du und ob du überhaupt sowas einsetzen kannst.

MFG

Sascha


----------



## Sebastian29 (8. April 2008)

OK, ich kann dir ja gerne ganz kurz beschreiben. Es handelt sich um einen JFrame als Suchmaschine, wo ich nach Ort und Name suchen kann und das Ergebnis der Suche wird in die JTable ausgegeben. Wenn ich bei Ort ein Sternchen eingebe, dann bekomme ich sämtliche Daten in der JTable ausgegeben und diese Daten habe ich komplett im Vector drin. Nach dem ButtonKlick "Excel exportieren" übergebe ich einfach dieses Vector in die Methode ( exportToExcel(....) )! Also, diese Methode habe ich so gebaut, dass ich überall verwenden kann. Mir geht es nur darum, dass ich bei großen Daten den Speicherverbrauch vermeiden will, um sauber in die Excel-Datei zu schreiben. Deshalb habe ich auch die o.g. Methode hier eingefügt.

Innerhalb der 2. Schleife kann ich ja nicht so abfragen, dass Daten alle 10000 in die Excel geschrieben werden muss oder?


```
StringBuffer buf = new StringBuffer();

            for( int row = 1; row <= tableModel.getRowCount(); row++ )
            {
                for( int column = 0; column < tableModel.getColumnCount(); column++ )
                {
			            if( row % 10000 == 0 )
			                workbook.write();

			            buf.append( tableModel.getValueAt( (row-1), column) );

                          if( cellFormat != null )
                                label = new Label(column, row, buf.toString(), cellFormat );
                        
                          if( label != null )
                                wSheet.addCell( label );
                        
                          buf.setLength( 0 );
                }
            }
```

Aber gut, wenn du mir nicht weiterhelfen kannst, muss ich dann weiter erforschen! Es war auch nicht böses gemeint! ;-)

Trotzdem Danke

Gruß
Sebastian29


----------



## zerix (8. April 2008)

Könntest du mal den StackTrace posten?

MFG

Sascha


----------



## Sebastian29 (8. April 2008)

Während der Schleifen kommt bei mir keinen Exception-Fehler, sondern die VM fängt an, mich 2 Mal anzuschreien:

java.lang.OutOfMemoryError
java.lang.OutOfMemoryError

und mein Programm wird abgestürzt, wo ich dort nichts machen kann!

Deshalb kann ich auch keinen StackTrace geben!

In der Dokumentation von Java Excel API Tutorial steht drin, was auch keinen Sinn macht:

Frequently Asked Questions

java.lang.OutOfMemory Exception

By default a JVM places an upper limit on the amount of memory available to the current process in order to prevent runaway processes gobbling system resources and making the machine grind to a halt. When reading or writing large spreadsheets, the JVM may require more memory than has been allocated to the JVM by default - this normally manifests itself as a java.lang.OutOfMemory exception.

For command line processes, you can allocate more memory to the JVM using the -Xms and -Xmx options eg. to allocate an initial heap allocation of 10 mB, with 100 mB as the upper bound you can use

    java -Xms10m -Xmx100m -classpath jxl.jar spreadsheet.xls 

In order to allocate more memory in this manner to servlets/JSPs, consult the help documentation for the Web Application Server.


----------



## Oliver Gierke (8. April 2008)

Kann es sein, dass diese Bibliotzhek einfach schlampig implementiert ist? Ich weiß nicht ob es eine gute Idee ist, Excel Dokumente zu erstellen, die so viele Zeilen haben. Bekanntlichermaßen ist ja auch bei den Spalten bei 200 und ein paar Zerquetschten Schluß. Kann gut sein, dass die Kollegen einfach gedacht haben: "so große Dokumente erzeugt eh keiner" und dementsprechend den Speicherverbrauch wenig beachtet haben. Wenn sowas schon in der Doku auftaucht, sieht das für mich genau danach aus.

Es gibt noch ein zwei andere Bibliotheken für das Handling von Office Files (http://www.rgagnon.com/javadetails/java-0516.html). Habt ihr die mal evaluiert?

Nur meine zwei 0,02€...

REINHAUN!


----------



## zerix (8. April 2008)

@Olli
Man kann insgesamt 230 Spalten und 65356 Zeilen im Exceldokument erstellen. Das müsste doch eigentlich ausreichen.


----------



## Oliver Gierke (8. April 2008)

Für Excel schon... vielleicht nicht für JExcel


----------



## Sebastian29 (8. April 2008)

@Oliver
Für Excel kann ich mir aber auch nicht vorstellen, mit einem Schlag 65356 Zeilen und 230 Spalten voller Daten zu speichern, sondern es wird auch bestimmt blockweise wegen Speicherverbrauch gespeichert! Und sowas muss man auch bei JExcel irgendwie implementieren! Sowie die gleiche Technik wie WeakReference! ;-)

Gruß
Sebastian29


----------



## zerix (8. April 2008)

Ich verstehe nicht ganz was du mit diesem Blockweise speichern hast. Die ganzen Daten befinden sich doch schon im Speicher, da bringt es nichts, das ganze "Stückchenweise" zu speichern.
Ich denke auch mal, dass das ganze nicht an JExcel liegt, sondern eher an deinem Programm. Wenn man mit einer 300kb-Datei 512 MB Speicher voll bekommt, kann da was nicht stimmen.



> ( z.B. 25.000 x 11 = 275.000, d.h. 275Kb Excel-Datei )



Wie kommst du eigentlich dadrauf?

Ich finde das immer so toll, dass Leute ihr privaten Nachrichten abschalten. 
Wenn du mir das Programm mal zukommen lässt, kann ich mir das vielleicht mal genauer anschauen. 

MFG

Sascha


----------



## Sebastian29 (9. April 2008)

So Leute!

Jetzt habe ich endlich festgestellt, warum ich 2 Mal Ärger bekommen habe und JExcel kriegt was auf dem Popo, bis er rot wird! 

Also, in der Klasse WorkbookSettings ist für die Excel-Datei bis zu 1MB standardmäßig eingestellt. Da habe ich auf 5 MB erhöht und es t! Keine Meckerei mit OutOfMemory! Es hat zwar etwas länger gedauert. Bei über 25300 Zeilen mit 11 Spalten ergibt die Größe der Excel-Datei 4.114 KB.


```
...
WorkbookSettings setting = new WorkbookSettings();
setting.setArrayGrowSize( 5 * (int)Math.pow( 1024, 2) );
...
```

@Oliver
Dieses Bibliothek JExcel finde ich doch gar nicht schlecht, man muss nur drauf kommen, wieso ich OutOfMemoryError bekommen habe! ;-)

Gruß
Sebastian29


----------

