Dienstag, 28. Oktober 2008

JTable Performance

Mich hat schon eine Zeit lang gewundert, warum Programme, die ich geschrieben habe und eine JTable benutzen, so viel CPU brauchen. Naja, inzwischen hab ich mal ein wenig rumgefrickelt und rausgefunden, dass diese Tabelle unter gewissen Umständen öfters geupdated wird, als das menschliche Auge überhaupt mitbekommt (> 50Hz).

Also hab ich einen kleinen Hack eingeführt, der wie folgt aussieht:


public class MyJTable extends JTable {

...
private long lastCall = System.currentTimeMillis();

public MyJTable () {
super();

new Thread() {
public void run() {
while(true) {
MyJTable.this.repaint();
try {Thread.sleep(20);}catch (Exception e) {}
}
}
}.start();
}

@Override
public void repaint () {
long curr = System.currentTimeMillis();
if (curr - LASTCALL > 20) {
super.repaint();
LASTCALL = curr;
}
}

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 :)