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.

LinkedList 33x schneller als ArrayList oder “Kenne deine API!”

Für meine Arbeit schreibe ich zum Test gerade ein kleines Plugin für ImageJ, indem ich unter anderem einen Region-Fill Algorithmus brauche – leider tut’s der filler von ImageJ hier nicht – also schnell was selbst zusammengehackt:

Startpunkt initialisieren und in eine Liste damit
while (Liste nicht leer) {
   hole ersten Punkt P aus der Liste
   prüfe Eigenschaften von p.x/p.y auf dem Bild
   if (Eigenschaften erfüllt){
      füge der Liste die Punkte darüber, darunter, rechts und links hinzu
   }
}

Vorher eine ArrayList entsprechend groß initialisiert, damit die nicht dynamisch wachsen muss und gut ist’s.

Moment – in vielen Iterationen werden hinten an der Liste 4 Punkte angefügt. Das geht mit der ArrayList schön schnell, wie man im ArrayList Quellcode unschwer erkennt (wenn die Liste groß genug ist). Aber in jeder Iteration wird vorne das erste Element entnommen. – Ein Blick in den ArrayList-Quellcode offenbart ein “System.arraycopy(elementData, index+1,  elementData, index, numMoved);“. Oha – das riecht eigentlich eher nach einer LinkedList.

Also: Methode profilen, Listentyp ändern, nochmal profilen (mit dem NetBeans Profiler ja kein Thema). Ergebnis: LinkedList ist wie erwartet viel schneller. Ohne meinen ganzen sonstigen Code reduziert sich der Testfall in etwa auf folgende Testklasse:

public class ListTest {

    public static void main(String[] args) {
        List al = new ArrayList(31000);
        List ll = new LinkedList();
        long a = System.currentTimeMillis();
        testWith(al);
        long b = System.currentTimeMillis();
        testWith(ll);
        long c = System.currentTimeMillis();

        System.out.println("ArrayList: "+(b - a) + " ms");
        System.out.println("LinkedList: "+(c - b) + " ms");
    }

    private static void testWith(List list) {
        list.add(new Point(0, 0));
        int i = 10000;
        Point p;
        while (!list.isEmpty()) {
            p = list.remove(0);
            if (i-- > 0) {
                list.add(new Point(p.x, p.y - 1));
                list.add(new Point(p.x - 1, p.y));
                list.add(new Point(p.x + 1, p.y));
                list.add(new Point(p.x, p.y + 1));
            }
        }
    }
}

Das Speedstepping am Notebook nicht vergessen, sonst fährt die CPU am anfang ja noch nicht auf Vollgas und schon rennt der Test. Die ArrayList habe ich auch extra noch mit mehr Elementen initialisiert, als jemals reinkommen. Ausserdem geht die Initialisierung auch nicht in die Messung ein (ist im Vergeich mit dem Rest zwar eh vernachlässigbar klein, aber damit niemand auf die Idee kommt, der Vergleich wäre unfair …).

Ausgabe:
ArrayList: 860 ms  = 100%
LinkedList: 31 ms  = 3,6%

Sieht man sich die API und den Quellcode der beiden Klassen an, verwundert das Ergebnis nicht wirklich. Der unterschied skaliert natürlich auch mit der Anzahl der Iterationen.

Fazit

Ist ArrayList also schlecht? – Klares NEIN! Nur für genau diesen Anwendungfall (vielfaches Einfügen am Ende, vielfaches Herausnehmen am Anfang, kein Zugriff auf Elemente irgendwo dazwischen nötig) ist die ArrayList der Speicherstruktur der LinkedList einfach unterlegen, die genau auf diesen Punkten optimal funktioniert.

Ergo: Know your API! Man sollte sein Handwerkszeug kennen und nicht blindlings einfach das verwenden was man sonnst auch immer nimmt und hoffen, dass Java ein allmächtiges Werkzeug ist, das einem alle Arbeit abnimmt (und dann nacher groß jammern, dass in C++ ja eh alles viel schneller geht). Und wenn man Zweifel hat, ruhig mal einen Blick in den Sourcecode werfen und ausprobieren 😉

Impact of Flash SSDs on Spatial Indexing

Neben Paros war ich auf der SigMod 2010 auch mit einer Veröffentlichung auf dem DaMoN Workshop (Data Management On new Hardware) vertreten. Der Titel der Veröffentlichung war “On the Impact of Flash SSDs on Spatial Indexing“.

Dass SSDs schnell und cool sind, ist ja mittlerweile allseits bekannt. Leider sind sie noch etwas teuer – nichts desto trotz haben sie den unglaublichen Vorteil, dass die Zugriffszeiten bei wahlfreien Lesezugriffen deutlich kleiner sind, da sie nahezu keine Seek-Zeiten aufweisen (also die Zeit in der der Lesearm der regulären Festplatte erst zur richtigen Position gefahren werden muss, bevor gelesen oder geschrieben werden kann). Diese Seek-Zeiten sind gerade bei Datenbankindices einer der bremsenden Faktoren. Da unser Lehrstuhl auf Datenbanken, DataMining und Indexing spezialisiert ist, haben wir uns daher angesehen, wie sich der R*-Baum (einer der Standard-Indices für mehrdimensionale Daten) auf SSDs im Vergleich zu HDDs verhält. Insbesondere, wie die Indices skalieren und wie es mit der Performanz aussieht, wenn höher dimensionale Daten ( > 10 Dimensionen bzw. Spalten) mit einem R*-Baum indexiert werden sollen und wie es sich mit dem Fluch der Dimensionalität verhält.

Fazit: War eine doch recht spannende und vor allem recht praktische Arbeit , da wir nicht einfach nur Modelle durchrechnen wollten, sondern wirklich die Zugriffe auf die Platte sehen und messen wollten – was die Tests auch sehr zeitaufwändig gemacht hat, da Ram und Platte eben doch einen “kleinen” Geschwindigkeitsunterschied haben.

Abstract:

Similarity queries are an important query type in multimedia databases. To implement these types of queries, database systems often use spatial index structures like the R*-Tree. However, the majority of performance evaluations for spatial index structures rely on a conventional background storage layer based on conventional hard drives. Since newer devices like solid-state-disks (SSD) have a completely different performance characteristic, it is an interesting question how far existing index structures proft from these modern storage devices. In this paper, we therefore examine the performance behaviour of the R*-Tree on an SSD compared to a conventional hard drive. Testing various in uencing factors like system load, dimensionality and page size of the index our evaluation leads to interesting insights into the performance of spatial index structures on modern background storage layers.

Die Veröffentlichung kann man auf unserer Website herunterladen: On the Impact of Flash SSDs on Spatial Indexing

Java Memory Model / MultiThreading in Java

Wer sich für das Java Memory Model und insbesondere für Multi Threaded Code und die Probleme mit denen man sich konfrontiert sieht, wenn man multi threaded programmiert interessiert, könnte das Video durchaus interessant finden.

Titel: Advance Topics in Programming Language: Java Memory Model
von Jeremy Manson

[youtube=http://www.youtube.com/watch?v=1FX4zco0ziY]

SwingX-WS mit JXMapKit auf der SIGMOD 2010 / PAROS: Pareto Optimal Route Selection

Zusammen mit meinen Kollegen haben wir eine Demo auf einer der wichtigsten Datenbank Konferenzen eingereicht, die ich letzte Woche in Indianapolis auf der SIGMOD (ACM Special Interest Group on Management of Data) zeigen durfte. Die Demo firmiert übrigens unter dem Titel PAROS: Pareto Optimal Route Selection.

Im Wesentlichen ging es dabei darum, einen von meinen Kollegen entwickelten Skyline-Algorithmus auf Straßennetzen anzuwenden um damit Wege unter mehreren Einschränkungen zu finden. Die klassische, einfache Wegsuche ist ja zum Beispiel “finde den kürzesten oder schnellsten Weg”. Bei der Skyline-Abfrage, geht es dabei darum, alle die Wege zu finden, die unter mehreren Attributen optimal sind. Also zum Beispiel alle kürzesten und schnellsten Wege, die gleichzeitig möglichst wenig Ampeln enthalten. Es müssen also mehrere Attribute gleichzeitig optimiert werden.

Da Datenmasse in der Wissenschaft oft Mangelware ist, habe ich zusammen mit etwas studentischer Hilfe einen Konverter für OpenStreetMap-Daten geschrieben um beliebig viele Daten erhalten zu können. Visualisiert wurde das Ganze mit Hilfe der JXMapKit-Komponente aus SwingX-WS! Die Gui-Entwicklung ging dank dem NetBeans Gui-Builder wie erwartet erfreulich einfach, so dass ich mich in der knappen Zeit auf die Integration des Algorithmus und auf die Architektur konzentrieren konnte. Die Architektur sollte es ermöglichen, Model und View  möglichst so zu kapseln, dass die Entwicklung und Integration neuer Algorithmen so einfach wie möglich und möglichst Unabhängig von jeglicher GUI-Programmierung ist, so dass auch Studenten schnell und einfach neue Algorithmen entwickeln und testen können.

Ich habe zwar (wie erwartet) nicht den Best-Demo-Preis bekommen, allerdings waren wirklich viele interessierte Leute auf den Demo-Sessions. Überrascht hat mich, dass ich sehr oft gefragt wurde, ob wir die Demo online stellen würden, bzw. ob die Demo OpenSource ist. Nach Rücksprache mit den Kollegen, kamen wir zu dem Schluss, dass das eine gute Idee sei und ich das machen werde. Ich werde in den nächsten Wochen also noch etwas den Code aufräumen, dokumentieren, online stellen und hoffen, dass jemand die Demo interessant und nützlich findet – vielleicht sogar so, dass es die Basis für ein oder mehre Zitationen bringt (ist immer wichtig bei Veröffentlichungen).

UPDATE: endlich ist der – äh – unschöne Code online. Da ich auch in absehbarer Zeit nicht die Zeit habe, ihn schön sauber und dokumentiert zu machen, kann ich ihn auch gleich online stellen. zum Download gehts hier lang.

Relevante Links:

Windows 7 (64bit) Druckerfreigabe mit XP Client (Canon i250)

Szenario:
An einem Windows 7, 64 Bit, soll der angeschlossenem Drucker über das Netz freigegeben werden.
Ein Windows XP, 32Bit, soll die Freigabe in Anspruch nehmen.

Problem:
Beim Verbinden des Druckers wird auf Windows XP-Seite eine Warnung ausgegeben, dass der Server keine Treiber zur Verfügung stellt.

Lösung:
Wie erwartet war ich nicht der einzige mit dem Problem. Die Lösung fand ich dann im computerbase-Forum.
Im Prinzip muss man Windows 7 “nur” beibringen, dass es auch einen 32Bit-Treiber anbieten soll.  Das funktioniert so, dass man den Windows 32bit-Treiber vom Druckerhersteller herunterlädt und am Drucker registriert: “Geräte und Drucker – *Druckername* – Druckereigenschaften – Freigabe – Zusätzliche Treiber”.

In der Praxis kommt es dann ab und an vor, dass genau das nicht funktioniert, weil der Drucker dann nicht gefunden werden kann und man eine Meldung erhält, dass kein Treiber für <Druckername> gefunden werden konnte. Wie im computerbase-Forum beschrieben, ist hier Handarbeit nötig. Die entpackten Treiber enthalten eine .inf-Datei. Diese muss man im Texteditor öffnen und den Druckernamen so abändern, dass der Name identisch mit dem Windows7-Druckernamen ist (also genau der Name, der in der Fehlermeldung genannt ist).

Im Falle meines Canon i250 sehen die betreffenden Zeilen der .inf-Datei dann so aus:

;WindowsXP
[Canon.NTx86.5.1]
"Canon Inkjet i250" = CNMI250XP, LPTENUMCanoni250F027, Canoni250
"Canon Inkjet i250" = CNMI250XP, USBPRINTCanoni250F027, Canoni250

“Canon Inkjet i250” ist dabei der Name des Druckers in Windows 7. Datei speichern und nochmal via “Geräte und Drucker – *Druckername* – Druckereigenschaften – Freigabe – Zusätzliche Treiber” installieren. Sofern das funktioniert, kann man den Drucker dann (sofern die Freigabe richtig eingestellt wurde) von Windows XP aus nutzen.

Sinn und Unsinn von E-Mail- und Link-Disclaimern

Wieder eine Mail erhalten mit dem zweifelhaften Text

Diese E-Mail und alle angehängten Dateien enthalten streng vertrauliche Informationen und sind lediglich für den/die in der Adressleiste genannte(n) Person(en) bestimmt. Sollte diese E-Mail bzw. deren Anhänge an Dritte und/oder nicht in der Adressleiste genannte Personen gelangen,  […]

Ich hab’ diesem Disclaimern ja noch nie wirklich vertraut. Ein Artikel bei heise (Disclaimer: Unnötiger Ballast für E-Mails) bestätigt die Vermutung, dass diese Disclaimer bei uns in Deutschland nicht wirksam sind. – Ebenso wie diese widersinnigen Distanzierungen von Links:

Mit Urteil vom 12. Mai 1998 hat das Landgericht Hamburg entschieden, daß man durch die Ausbringung eines Links […]

Auch hier gibt es eigentlich diverse Seiten, die erklären, dass das Urteil vom LG Hamburg eben genau das nicht tut, was die Links versprechen: und zwar den Seitenbetreiber schützen. Mehr Informationen gibt es unter anderem auf diesen Seiten: