| 

.NET C# Java Javascript Exception

5
Hi, ich habe gerade ein etwas größeres Programm zu warten und muss einen Fehler fixen der ein Tomcat Programm abstürzen lässt. Ich hab das Problem zurückverfolgt und weiß nun wo es liegt, allerdings wird einfach nur eine ArrayList mit ca 2870 Objekten gefüllt die danach in eine Excel Datei geschrieben werden soll.
Ohne jetzt das komplette Programm umzuschreiben, wollte ich eine Tempfile bzw. serialisierte Datei anlegen die ich später wieder Zeilenweise einlesen / deserialisiere und in die Excel Datei schreiben kann.

Geht das überhaupt oder muss ich wohl oder übel das komplette Programm umstricken? Ich hab gerade den Code nicht da, aber ich kann den Auszug einmal posten.

Edit: hier der Code, ich hab gestern noch die Tempfile hinzugefügt, hat aber noch nicht funktioniert. Werd das heute in Angriff nehmen.

private class ReportExcel {
private ArrayList datenArray = new ArrayList ();


public ReportExcel(Integer fgNr) throws IOException {
String tempFile = "c:\temp\save.it" ;
reports4Auswertung = getReports4Auswertung();

if (reports4Auswertung != null) {
FileOutputStream o = new FileOutputStream(tempFile) ;
ObjectOutputStream oo = new ObjectOutputStream(o) ;

for (Iterator i = reports4Auswertung.iterator(); i.hasNext();) {
Report report = (Report) i;
ReportDaten daten = new ReportDaten();
boolean success = daten.create(report, fgNr);
if (success == true)
logger.debug("Nummer " + i.toString());

//datenArray.add(daten); // TODO: HEAP Space Error durch zu große ArrayList
oo.writeObject(daten) ;
i.remove();
}

oo.flush() ;
oo.close() ;
}
}
}


Edit2:
Immernoch keine Verbesserung.
Ich hab einmal nachverfolgt was diese Klasse aufruft und fand einen einzige Aufruf -.-
Ich stricke die Schleife nun um das Workbook und sehe das ich es so zum laufen bekomme.

@puls200
Ich konnte auf deinen Kommentar nicht kommentieren :)
Also den Garbage Collector manuell aufzurufen hat "ein bisschen" was gebracht. Von den gerade mal 2800 Reports hat er nun fast doppelt so viele in den Speicher schreiben können wie vorher... ganze 720. Leider bringt das auch nichts.

Bevor die Antwort kommt ich solle den Heap Space vom Tomcat erhöhen: Der ist auf maximum und ich will ein Softwareproblem nicht mit mehr Hardware lösen.

Wie gesagt muss ich jetzt die Excel Schreibfunktion auseinander nehmen, die füllt drei Bildschirme und das Format wird vom Kunden so erwartet, also mal sehen ob das so klappt wie ich will.
19.03.2012
Lord_Pinhead 778 1 8
Verwendet er Excel selbst (per COM?) zur Generierung? Vielleicht liegts daran, dass die Referenz nicht losgelassen wird. Dann könntest du versuchen, in jedem Durchlauf explizit eine Instanz zu erzeugen (und wieder schliessen!). Sonst ist schwer dazu was zu sagen ohne den Code zu sehen ;)
puls200 20.03.2012
4 Antworten
2
Wenn es wirklich an der großen ArrayList liegt wird das nix bringen. Warum muss das Programm alle Objekte gleichzeitig im Speicher halten? Könnte man nicht hergehen und ein Objekt direkt schreiben statt es der Liste hinzuzufügen? Solange die Liste noch einer Referenz hält kann der Speicher nicht freigegeben werden. Ich hoffe ich habe mich halbwegs verständlich ausgedrückt, wird schon spät.. :)
19.03.2012
puls200 3,8k 7
So einigermassen :)
Eigentlich war das ja gerade meine Idee wenn ich dich richtig verstehe. Ich hab den Code mal aufgeräumt und an meine Frage angehängt.
Lord_Pinhead 20.03.2012
1
Deine Änderung hat keine Verbesserung ergeben? Dann muss das Speicherproblem woanders liegen. Die Klasse ReportDaten erzeugt keine externen Referenzen? Probier mal die Garbage Collection manuell auszulösen: System.gc(); bzw. System.runFinalization();
puls200 20.03.2012
1
Abschlussinfo:
Ich hab die ganze Zeit an der falschen Stelle gesucht. Das Problem bestand in der Hauptsache bei der JNDI-Datenbankverbindung. Ablauftechnisch wurde hier die Verbindung aufgebaut und dann die Aufgaben abgearbeitet. Die Verbindung und ResultSets wurden nach dem Beenden trotzdem nicht dereferenziert (rs.close) weil die Datenbankverbindung (Connection con..) nicht geschlossen wurde, dadurch kam es zum Memory Leak.

Darauf gekommen bin ich mit Visual VM, nachdem ich mehrere Heapspace Abzüge und Snapshots verglichen hatte.

Lösung ist nun im einzelnen:
- Wie schon geschrieben Iteratoren zu verwenden und Einträge per it.remove zu entfernen
- Finalizer in den Inline Klasse
- Jede Aufgabe baut eine erneute Datenbankverbindung auf und löst diese am Ende
- das Workbook inkl. dem POI Dateisystem wird am Schluss im finally-Block dereferenziert (Speicherverbrauch war ca. 300 MB pro Workbook)
- Jede Exceldatei hat eine "eigene" Datenbankverbindung die nach Abschluss und Schreiben der Datei geschlossen wird

Mit den ganzen "verrenkungen" wie viele nun sagen würden, halte ich den Speicherverbrauch bei unter 250-350 MB was bei der Angeforderten Datenmenge gerecht wird und die CPU Last ist nun minimal gestiegen. Sollte vielleicht mal jemand das selbe Problem haben, sucht man nach Visual VM Memory Leak Detection, da gibt es eine gute Anleitung diesbezüglich.

Danke für die Hilfe, das mit den Referenzen war wirklich der Hinweis der zum Erfolg führte.
12.04.2012
Lord_Pinhead 778 1 8
0
Moin zusammen,

ist vielleicht eine blöde Antwort, aber wie wäre es, den -Xmx Parameter höher zu setzen?

Nachtrag: Grundsätzlich ist es besser das Loch zu finden, als es mit noch mehr Speicher zu versorgen. Hast du schon mal an die Verwendung eines Profilers gedacht?

Gruß Karl
20.03.2012
Karl 958 8
Hi Karl,

ich brauch kein Profiler mehr, ich hab das Problem schon gefunden. Nur leider will die JVM mir meinen Speicher nicht wiedergeben auch wenn ich die Referenzen auflöse und die Objekte nach Nutzung mit objekt = null leere.
Wie ich schon geschrieben hab ist der Server auf dem das läuft am Ende des Physikalischen Speichers, laufen ja noch mehr VM´s drauf und müssen sich den Arbeitsspeicher teilen. Aktuell sind 1024MB Speicher für den Tomcat zugewiesen und das Programm futtert den wie Smarties :)
Lord_Pinhead 21.03.2012
Ich hab Visual VM als Profiler eingesetzt und zum Erfolg gekommen, der vermutete Teil war nur ein kleiner Teil des Problems.
Lord_Pinhead 12.04.2012
Ist die Lösung was für das allgemeine Interesse?
Karl 13.04.2012
0
So, ich hab nun versucht die Auswertung direkt zu schreiben, verwendet wird nicht Excel direkt, sondern ein Framework von Apache -> http://poi.apache.org/spreadsheet/index.html

FileInputStream fis = new FileInputStream(temp);
POIFSFileSystem fs = new POIFSFileSystem(fis);
HSSFWorkbook workbook = new HSSFWorkbook(fs);
HSSFSheet sheet = workbook.getSheetAt(0);
....


Beim füllen der Zellen wird ein Iterator verwendet, also was liegt näher als den dann mit it.remove() bei jedem durchlauf zu verkleinern.
Jetzt entferne ich bei jedem durchlauf die verwendete Zeile, entferne beim Erstellen der Dateien auch die jeweiligen Zeilen damit der GC sie entfernen könnte, will er aber nicht.
Danach habe ich alle Veriablen genullt (array=null; report = null;.....) und den GC manuell aufgerufen -> keine Chance das er den Speicher freigibt.

Jetzt habe ich gestern noch folgendes übles Konstrukt gebaut (ich komme dafür in die Programmiererhölle)

freeMem = r.freeMemory();
if(freeMem <= minRunningMemory ) {
logger.info("Lasse GC laufen");
freeMem = r.freeMemory();
System.gc();
logger.info("Freier Speicher nach GC: "+ freeMem);

while(freeMem <= maxRunningMemory) {
logger.info("Schlafe für 100 sekunden");

try {
Thread.sleep(100000);
freeMem = r.freeMemory();
System.gc();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}


So hat der Job bzw. der GC etwas Zeit zwischen den Aufrufen und das brachte sogar etwas. Trotzdem ist das ganze mehr als Schwachs..... und ich werde den Reportjob aufsplitten damit er 5 einzelne Jobs macht. Dabei muss ich nur den Job so belassen wie er ist und die Daten die ich möchte übergeben, dann kann ich Inkrementell arbeiten und sollte damit besser fahren.

Ich finde es trotzdem immernoch eine Frechheit das man nicht einfach die Ojekte einfach dereferenzieren kann und der GC sie entfernt. Selbst das Ändern des GC verhaltens half nichts:
-Xms1024m -Xmx1024m
-verbose:gc
-XX:+PrintGCTimeStamps
-XX:+PrintGCDetails
-XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode
-Xprof


Vielleicht findet sich der Fehler trotzdem noch, werde die Woche weniger dazukommen das zu fixen :)

Ersteinmal danke für die Hilfe, vielleicht hat noch jemand eine Idee die ich übersehen habe.
21.03.2012
Lord_Pinhead 778 1 8
1
Die Verrenkungen sollten m.E. gar nicht nötig sein, ich glaube immer noch das irgendwo Referenzen gehalten werden. Du hast auch *alle* Streams mit close() geschlossen?
puls200 21.03.2012
Ich werde nochmal durch alles durchgehen, aber alle Filestreams sind auf jeden Fall geschlossen. Wenn ich morgen oder nächste Woche Zeit habe, lasse ich nochmal einen Reporter laufen, bisher nutze ich Visual VM. Wenn jemand etwas besseres kennt im Referenzen aufzuzeigen, dann her damit. Im Test sah ich nur das ein gewaltiges Primitives Byte Array gewachsen ist, aber bei 4 Millionen Einträgen waren in der Stichprobe nur HashMaps drin, und die nutze ich nicht.
Lord_Pinhead 22.03.2012

Stelle deine Java-Frage jetzt!