How to create Memory Leaks by using Inner Classes.

The most recent Java Specialists Newsletter finally convinced me to start this post that I was having in mind for quite some time.

One of the really huge advantages of Java is that you almost do not have to care about cleaning up your memory as the Garbage Collector usually does this for you as soon as Objects are no more referenced. Usually this works really really well so that you really don’t have to care about annything! But maybe once in a while you may be observing something like a memory leak. Some people then call the Garbage explicitly – which is usually just a bad idea and possibly also doesn’t help either so that the “leak” remains. The better solution in this case would be profiling so that you can see why some classes are not cleaned up.

A nice source for memory leaks can be the use of anonymous inner classes. Assume the following class where you want to compute s.th and return a Result-Object which derives from an Interface:

interface Result{}
class Outer {
    int[] data = null;
    public Outer(int s) { data = new int[s]; }
    Object getResult() { return new Result(){}; }
}

So if you call new Outer(1).getResult(), you will still have an instance of Outer in memory even though you did not keep an explicit reference. As explained in the Java Specialists Newsletter, each instance of an anoymous inner class always keeps a reference to the outer class! This is not a big deal as long as

  • you don’t keep a lot of data in the Outer instance or
  • if the lifetime of the Result object is not long or
  • if you won’t create a lot of results anyways.

Let’s have an example. If you execute

ArrayList l = new ArrayList();
int i = 0;
while(true){
    l.add(new Outer(0).getResult());
    System.out.println(i++);
}

with the above classes without memory constraints (-Xmx), this will run for quite some time because you are only holding 2 class references (Outer, Result) 1 field (the emtpty data array) and an implicit reference from Result to Outer. Which makes a total of 48 bytes on my Win7 64bit machine (according to this measurement).

Now change the parameter in the constructor of Outer from 0 to 100000 and execute the code again. In my case I am getting an OutOfMemoryException after a bit more than 2000 created instances as now suddenly each iteration consumes 400.048 bytes (48 bytes as before + 100.000*4 bytes for the int-array) even though we only keep the explicit reference to the Result objects!

So – if you are creating an inner class the next time – you might have a brief look at the outer class as well and think about memory consumption and lifetime.

Speicherverbrauch in Java oder “Size does matter!”

Eines der Vorurteile gegenüber Java ist ja der (angeblich) enorme Speicherverbrauch. Frage ich dann, ob denn die entsprechende Applikation schon mal auf Speicherverbrauch geprofiled wurde und welcher Profiler verwendet wurde, gibt es große Augen und Gestammel über HeapSize, OutOfMemoryExceptions und dass in C ja eh alles besser sei. Na, da weiß man doch gleich dass nicht nur Java schuld sein muss. – Hat man mal mit ImageJ gearbeitet, ist man ab und an schon äußerst verwundert, wie schnell Java sein kann – und fühlt sich auch angespornt seine eigenen Kenntnisse  zu erweitern.

Generell gibt es zur Speicherdiskussion (mindestens) zwei Szenarien.

  1. Die Diskussion, wieviel Speicher eine Datenstruktur verbraucht und ob nicht eine andere Speicherstruktur besser ist.
  2. Wir haben eine OOME und müssen sie beseitigen.

Wieviel Speicher verbraucht meine Datenstruktur?

DAS ist jetzt mal wirklich ein Nachteil von Java wie er im Buche steht, denn es gibt per se keine wirklich einfache Möglichkeit, den Speicherverbrauch einer Struktur oder eines Objektes zu ermitteln. Der Artikel From Java code to Java heap ist hier definitiv anzuraten. Hier wird detailierter über den Speicherverbrauch von Klassen eingegangen.

Im Java Specialists Newsletter von 2001(!!) gibt es einen einen Workaround und ein paar weitere Erklärungen – es folgt eine teils Übersetzung aus dem Newsletter:

  1. Jede Klasse belegt mindestens 8 Bytes auf dem Heap (also auch ein new Object()).
  2. Jeder Datentyp belegt 4 Bytes, außer long und double, die 8 Bytes belegen. Achtung, das heißt, auch ein Datentyp byte benötigt dann 4 Bytes. Außerdem wird der Speicherverbrauch nur in 8 Byte Schritten erhöht. D.h eine Klasse mit einem byte Datentyp belegt 8 Byte für die Klasse + 8 Byte für die Daten = 16 Byte.
  3. Arrays sind speicherschonender  – der geneigte Leser darf das gerne selbst nachlesen.
  4. String#intern() kann ordentlich Speicher sparen.
  5. Boolean.TRUE und Boolean.FALSE benötigen weniger als new Boolean(true).

Okay, alles schön und gut. Aber eine komplexere Struktur benötigt dann wieviel genau? Im Java Specialists Newsletter ist dazu eine kleine aber feine Klasse, die hier weiterhilft:

public class MemoryTestBench {

    public long calculateMemoryUsage(ObjectFactory factory) {
        Object handle = factory.makeObject();
        long mem0 = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
        long mem1 = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
        handle = null;
        System.gc();System.gc();System.gc();
        System.gc();System.gc();System.gc();
        System.gc();System.gc();System.gc();
        System.gc();System.gc();System.gc();
        mem0 = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
        handle = factory.makeObject();
        System.gc();System.gc();System.gc();
        System.gc();System.gc();System.gc();
        System.gc();System.gc();System.gc();
        System.gc();System.gc();System.gc();
        mem1 = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
        return mem1 - mem0;
    }

    public void showMemoryUsage(ObjectFactory factory) {
        long mem = calculateMemoryUsage(factory);
        System.out.println(factory.getClass().getName() + " produced " + factory.makeObject().getClass().getName() + " which took " + mem + " bytes");
    }
}

Die ObjectFactory sieht so aus:

public interface ObjectFactory { public Object makeObject(); }

Und am Beispiel einer ByteFactory sieht das wiederum so aus:

public class ByteFactory implements ObjectFactory { 
   public Object makeObject() { return new Byte((byte)33); } 
}

Jup, etwas aufwändig. Aber immerhin ein Ausweg aus dem Dilemma um hypothetische Diskussionen was wohl besser sein könnte. Und ja, DAS ist in C wirklich einfacher – und trotzdem will ich weiter Java verwenden 😀

OMG, OOME – was tun?!

Regel 1: NICHT blindwütig irgendwo irgendwas irgendwie anders machen.
Regel 2: Auch nicht nachdenken und glauben zu wissen wo es weh tut.

Sondern? Messen!
Zum Beispiel mit der JVisualVM, die bei neueren SUN/Oracle JDKs (nicht JRE) bereits im bin-Verzeichnis der JVM mitgeliefert wird. Ist man eh mit NetBeans unterwegs, kann man das Programm auch nochmal von dort direkt profilen. Ich persönlich setze den erlaubten Speicher (also -Xmx) dann gleich kleiner an, damit die OOME schneller eintritt und weniger Daten betrachtet werden müssen.

Mit etwas Glück sieht man dann recht schnell, welche Datenstrukturen (zu) oft im Speicher sind und kann dann gezielter nach den Referenzen suchen, die es eigentlich gar nicht geben sollte.