Dienstag, 28. Oktober 2008

Stranges JAVA GC-Verhalten

Also manchmal schüttel ich echt den Kopf...

Mir wurde immer beigebracht, ein großer Vorteil von Java sei, dass man sich nicht um Speicherverwaltung kümmern brauch. Nun, dem scheint nicht ganz so zu sein. Ich habe das Problem, dass ich viel Daten in einer Liste speichere, diese werden ab einem bestimmten Zeitpunkt nicht mehr benötigt und ich kann sie löschen. Leider gibt Java den dazu alloziierten Speicher nicht frei und rennt lieber in einer OOMemory Exception :(

Nach ein wenig rumprobieren, hier die Lösung: System.gc() genau 4x aufrufen! Fragt mich bloß net, warum genau 4x, aber anscheinend scheint das des Rätsels Lösung zu sein :)

Hier mal ein Beispielcode:

import java.util.*;
public class NewTest {

public static void main(String[] args) {
System.out.println("before doing anything! " + Runtime.getRuntime().totalMemory());
Bla b = new NewTest().new Bla();

System.out.println("after instantiating class! " + Runtime.getRuntime().totalMemory());
b.allocateALotOfMemory();
System.out.println("after allocating a lot of memory! " + Runtime.getRuntime().totalMemory());

// I want to free the memory, allocated by the list!
b.testList = b.testList.subList(0, 1);
b.testList.clear();
b.testList = new ArrayList(1);
b.testList.retainAll(new ArrayList(1));
b.testList = null;
// what else should I do?!?! :)

System.out.println("nothing happened?!?! " + Runtime.getRuntime().totalMemory());

for (int i = 1; i <= 4; i++) {
System.out.println("before " + i + "th run of GC " + Runtime.getRuntime().totalMemory());
System.gc();
System.out.println("after " + i + "th run of GC " + Runtime.getRuntime().totalMemory());
}
}

private class Bla {
public List testList;
public Bla () {
testList = new ArrayList(1);
}
public void allocateALotOfMemory() {
for (int i = 0; i < 1000; i++) {
String tmp = "";
for (int j = 0; j < i; j++) tmp += "" + j;
testList.add(tmp);
}
}
}
}


Output:

before doing anything! 2031616
after instantiating class! 2031616
after allocating a lot of memory! 4591616
nothing happened?!?! 4591616
before 1th run of GC 4591616
after 1th run of GC 4591616
before 2th run of GC 4591616
after 2th run of GC 4337664
before 3th run of GC 4337664
after 3th run of GC 3416064
before 4th run of GC 3416064
after 4th run of GC 2035712


PS: Wenn jemand eine Idee hat, warum das net klappt oder wie man es besser machen kann, immer her damit :)

1 Kommentar:

Unknown hat gesagt…

Zu Anfang (kleines Projekt, Beispielcode, Experimente,...) muss man sich um die Speicherverwaltung sicherlich keine Gedanken machen. Irgendwann, egal ob nun Java, Python, Ruby, ..., kommt halt der Punkt, an dem man verstehen muss, wie die Garbage Collection funktioniert und wo sie auf ihre Grenzen stoesst. Das unterscheidt die Leute, die aus Spass programmieren und ewig Durschnitt bleiben (klingt hart, aber ist wohl so), von denjenigen, die das Programmieren etwas professioneller nehmen bzw. ihren Lebensunterhalt damit verdienen und wirklich Ahnung haben (muessen).

http://media.pragprog.com/titles/mjwti/specialist.pdf
spielt gut auf diesen Sachverhalt ein.
Zitat: "To start the interview, I asked this person (and all the others
I had interviewed that week) to rate himself on a scale of one to ten. He
said nine. I’m expecting a star here. If this guy rates himself so high, why can’t
he think of a single abusive programming trick that would cause a JVM to crash?"

Dudel hat das Buch zufaellig (von mir ausgeliehen).

Also mehr davon, Germi!

Gruesse,
Robert