JExcel OutOfMemory bei ca. 25.000 Zeilen?

Sebastian29

Erfahrenes Mitglied
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
 
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
 
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:

PHP:
	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();
		}
	}
 
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
 
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.*; !

PHP:
		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
 
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
 
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?

PHP:
            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
 
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.
 
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!
 

Neue Beiträge

Zurück