Hilfe, OutOfMemory Exc. Heapspace bei DB export

Ich habe jetzt über eine Stunde das Internet durchforstet und konnte mein Problem leider noch immer nicht lösen.

Es geht um folgende Ausgangssituation:

Ich führe in meinem Programm eine etwas ausführlichere Datenbankabfrage durch, dass Ergebnis wird in einem ResultSet gespeichert. Nun wird dieses ResultSet an eine Methode übergeben, um den Inhalt ins XML-Format zu speichern. Also praktisch das die Ergebnistabelle der Abfrage 1 zu 1 zu .XML

Der export dauert schon ziemlich lange, jedoch kann ich anhand von System.out.println erkennen, dass er arbeitet. Nach ca 1 Minute bekomme ich ständig eine OutOfMemoryException, obwohl ich bereits 1024M der VM als Argument übergeben habe.

Im SQL-Statement habe ich bereits zu Testzwecken “WHERE ROWNUM < 100” eingefügt, dennoch packt es die VM nicht.

Hier noch der Auszug des Codes, eventuell könnt ihr mir ja helfen, den Code zu gunsten von Speicher in der VM zu verbessern. Bin leider kein Java-Profi.

private void exportToXLS(ResultSet rs){
		  String result = null;
		  System.out.println("start exporting");
	        try {
	            Document root = new Document(new Element("DBExport"));
	          
	            ResultSetMetaData metaData = rs.getMetaData();

	           //Alle Datensätze durchgehen
	            while (rs.next()) {
	            	
	                Element row = new Element("Artikel");
	               //Dem Rootelement, ein neues Childelement zuweisen
	                root.getRootElement().addContent(row);

	               //Zuweisung der Attribute
	                for (int i = 1; i <= metaData.getColumnCount(); i++) {
	                   
	                    Element val = new Element(metaData.getColumnLabel(i));
	             
	                   //Zuweisung der Werte
	                    val.setText("" + rs.getObject(i));
	                    System.out.println(rs.getObject(i));
	                    row.addContent(val);
	                    val = null;
	                }
	                
	               
                    row = null;
	            }

	            XMLOutputter out = new XMLOutputter();
	            
	            out.setFormat(Format.getPrettyFormat());
	           
	            result = out.outputString(root);
	            
	            //aktuelles Datum parsen für den Dateinamen
	            Date dateNow = new Date ();
	    		SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd");
	    		StringBuilder now = new StringBuilder( dateformat.format( dateNow ) );
	    		String date = now.toString();
	            
	    		out.output(root, new FileOutputStream(filepath+"/" + date +".xml"));
	            System.out.println("export done");
	            System.exit(0);
        	
	        } catch (Exception e) {
	            e.printStackTrace();
	            System.exit(0);
	        }
	}

P.S:

Das Programm hatte mit anderen Abfragen welche ebenfalls >3000 Datensätze lieferte keine Probleme. Die Dateien wurden sauber ins XML-Format übertragen und gespeichert.:grr:

Ich danke euch !

Gibt die OutOfMemoryException eine Codezeile an, die weiterhelfen kann?

Werden die schließbaren-Objekte auch geschlossen? Das SQL-Statement, das ResultSet, der XMLOutputter…
Wie groß ist das SQL-Ergebnis in Zeilen und Spalten? Immerhin sind hier zwei verschachtelte Schleifen die immer ein new machen.

Ansonsten fällt mir an dem Code erst einmal nichts ins Auge.

Wo genau in diesem Code erhälst Du denn den OOME?

Unabhängig davon würde ich 2 Ansätze sehen, den Speicherverbrauch zu reduzieren:[ol]
[li]Nutze SAX stat DOM, also schreibe das XML währen Du das Resultset verarbeitest.
[/li][li]vermeide unnötige Objekterzeugung. Beispielsweise in Zeile 23: statt val.setText("" + rs.getObject(i)); besser val.setText(rs.getString(i)); oder val.setText(rs.getObject(i).toString()); Deine Variante erzeugt auf jeden Fall ein neues String-Objekt. Variante 1 verläßt sich darauf, dass der JDBC-Treiber in dieser Hinsicht effektiv programmiert ist und Variante 3 ist unter der Bedingung besser, dass der JDBC-Treiber das Objekt als String vorhält (was ziemlich wahrscheinlich ist).
[/li][/ol]
bye
TT


Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at oracle.jdbc.driver.CharCommonAccessor.getString(CharCommonAccessor.java:463)
	at oracle.jdbc.driver.T4CVarcharAccessor.getString(T4CVarcharAccessor.java:454)
	at oracle.jdbc.driver.CharCommonAccessor.getObject(CharCommonAccessor.java:788)
	at oracle.jdbc.driver.T4CVarcharAccessor.getObject(T4CVarcharAccessor.java:986)
	at oracle.jdbc.driver.OracleResultSetImpl.getObject(OracleResultSetImpl.java:1094)
	at WitteWebshopExport.exportToXLS(WitteWebshopExport.java:193)
	at WitteWebshopExport.getDataSet(WitteWebshopExport.java:154)
	at WitteWebshopExport.initialize(WitteWebshopExport.java:56)
	at WitteWebshopExport.<init>(WitteWebshopExport.java:51)
	at WitteWebshopExport.main(WitteWebshopExport.java:230)

Die Zeile 193 bezieht sich auf Zeile 22 meines Code-Auszugs. Wie in meinem Code zu sehen, wird das Programm nach Ende des Exportvorgangs beendet. Doch soweit kommt es erst garnicht. Ich weiss nicht, wie ich die Objekte freigeben soll, dass einzige was mit in den Sinn kam, war “row” und “val” nach jedem Durchlauf auf null zu setzen.

Ich nehme an, das Posting überschneidet sich mit TTs. Aber SAx statt DOM sollte schon reichen.

Also mit der getString() Variante statt getObject funktioniert es leider auch nicht. Wenn ich SAX nehme, muss ich sicherlich den Code umschreiben sehe ich das richtig ?

Wie könnte ich denn Objekte in der Methode die nicht gebraucht werden freigeben, bzw an welcher Stelle ist es sinnvoll ?

[QUOTE=0plan]Ich weiss nicht, wie ich die Objekte freigeben soll, dass einzige was mit in den Sinn kam, war “row” und “val” nach jedem Durchlauf auf null zu setzen.[/QUOTE]Das bringt gar nichts. Die Variable verfällt eh am Ende des Blocks.

Besser wäre, die for Schleife in eine eigene Methode auszulagern. Ich meine mich zu erinnern, dass der GC nach dem Ende einer Methode loslaufen kann. Vielleicht verhilft auch ein Thread.sleep(2)zu der Pause, die der GC zum Eingreifen braucht.

Aber egal, der Umstieg auf SAX wird definitiv mehr bringen.

bye
TT

Ich werde es später mit SAX probieren, leider muss ich dann wieder alles umschreiben, was mir als Anfänger nicht gerade leicht fällt und die Zeit knapp ist. Kann mir nicht vorstellen, dass die API JDOM nicht in der Lage ist, eine größere Menge zu übertragen.

Die Auslagerung der Schleife in eine neue Methode brachte leider auch keinen Erfolg.

[QUOTE=0plan]Ich werde es später mit SAX probieren, leider muss ich dann wieder alles umschreiben, was mir als Anfänger nicht gerade leicht fällt und die Zeit knapp ist.[/QUOTE]Mach es jetzt. Du wirst nie Zeit dafür haben und der Aufwand wird immer weiter steigen.

[QUOTE=0plan;29131] Kann mir nicht vorstellen, dass die API JDOM nicht in der Lage ist, eine größere Menge zu übertragen. [/QUOTE]Es geht nicht um die Übertragung der Daten. Das macht der JDBC-Treiber.
Es geht um den Speicherverbrauch Deiner Anwendung. Und da unterscheiden sich DOM- und SAX-Frameworks.

Und hier ist die für Dich wichtigste Lektion aus diesem Vorfall: Du als Entwickler musst Dich vor dem Programmieren darüber informieren welche vor und Nachteile die zur Auswahl stehenden Frameworks haben und dem entsprechend das am besten passende auswählen. In Deinem Fall ist der wahlfreie Zugriff auf einzelne Knoten im XML-Baum nicht notwending. Deshalb was ein DOM-basiertes Framework eine schlechte Wahl…

bye
TT

Ansonsten, musst du den Profiler anschmeissen. Ich denke auch, dass hier der Hase woanders versteckt ist. Eventuell ist die Speicherung des ResultSets und des DOM-Baums zuviel.

Hab den Fehler selbst gefunden, es gab Probleme beim Einlesen des Strings welcher die Abfrage speichert. Somit kam ich auf 920.000 Datensätze anstatt wie gewollt 60.000

Danke für eure Hilfe !

Wenn die Daten noch wachsen kannst du später aber leicht vor dem selben Problem stehen.

Gruß

Das ist richtig, jedoch ist weiss ich das es nicht dazu kommt. Also kann ich mir den Aufwand sparen das gesamte Programm neuzuschreiben damit es SAX nutzt. Für die nächsten Projekte werde ich von Beginn an SAX nutzen.

Danke