Autorenarchiv

JDK Development Tools

Freitag, 01. Oktober 2010

Vor einigen Tagen bemerkte ich ein ungewöhnliches Verhalten von einer unserer Tomcat Serverinstanzen. Der Server schien normal zu laufen, aber ein Kern der CPU wurde bereits seit einigen Minuten zu 100% belastet. Die Anwendung war ganz normal erreichbar und auch in der Logausgabe waren keine Unregelmäßigkeiten zu finden. Eine Kopie der Produktivinstanz lief in der Testumgebung problemlos. Es handelte sich also um ein Problem, das erst zur Laufzeit entsteht und nicht mit dem Datenbestand o.ä. zusammenhängt. Ein Neustart würde das Problem wohl beheben – aber tritt es dann eventuell wieder auf? Hat ggf. ein Anwender nur einen sehr umfangreichen Report gestartet und der Server braucht für die Verarbeitung einfach sehr lange oder handelt es sich um einen Bug in der Software?

Für mich stellte sich also die Frage, wie ich nun herausfinde, was der Server dort gerade macht. Ein Neustart kam aber nicht in Frage, da das Problem dann verschwinden würde und eine Fehleranalyse unmöglich wäre.

Eine Recherche im Internet führte mich schneller als erwartet zum Ziel. Das aktuelle Java SDK bringt eine ganze Reihe von Tools zur Überwachung von Javaprozessen mit. Eines dieser Tools nennt sich jstack und zeigt mir den aktuellen Stack aller Threads innerhalb eines Prozesses an. Das schuldige MemoryLeak war schnell gefunden und gefixed. Alles was ich brauchte war also bereits auf dem Server installiert.

Ein Blick auf die JDK Develoment Tools lohnt sich also in jedem Fall. Gerade wenn man keine Möglichkeit mehr hat, ein Monitoring Tool zu installieren oder man sich nicht sicher ist, was da gerade läuft, können einem diese (meist schon vorhanden) Tools weiterhelfen.

(jstack ist wohl leider noch nicht im Windows JDK enthalten. Alternativ kann man dort aber die grafische jconsole verwenden. Auch diese ist im JDK enthalten.)


Felix Breske


Hibernate Proxies und Polymorphismus

Freitag, 07. Mai 2010

Beim LazyLoading mit Hibernate werden die eigentlichen Entitäten durch Proxy Objekte ersetzt. Diese Proxy Objekte werden erst dann initialisiert, wenn auf sie zugegriffen wird. Hibernate erstellt eine Unterklasse der Entität und überschreibt alle Methoden der Elternklasse. Wird eine dieser Methoden aufgerufen, so delegiert der Proxy die Anfrage an die Datenbank und füllt die Entität aus der Datenbank. Diese Objekte lassen sich mit instanceof auf ihren Typ prüfen und sie können in ihren Ursprungstyp gecastet werden, da es sich ja bei dem Proxy um eine Kindklasse der eigentlichen Entität handelt.

Wenn jetzt aber Objekthierarchien und Polymorphismus mit ins Spiel kommen, dann wird es schwieriger.

Ein Beispiel:

public class Vehicle{
...
}
 
public class Car extends Vehicle{
   public void reFuel(){};
}
 
public class Bicycle extends Vehicle{
...
}
 
public class Garage {
   Set<Vehicle> vehicle = new HashSet<Vehicle>();
   public Set<Vehicle> getVehicle(){
       return vehicle;
   }
}

Alle Autos sollen nun aufgetankt werden.
Eine einfache Möglichkeit wäre:

for(Vehicle v : garage.getVehicle()){
   if(v instanceof Car){
       Car car = (Car)v;
       car.reFuel();
   }
}

Wenn jetzt aber Hibernate mit LazyLoading eingesetzt wird, so werden wir leider bei nächster Gelegenheit mit dem Auto ohne Benzin liegen bleiben. Hibernate ersetzt die einzelnen „Vehicle“ durch Proxies, also durch Kindklassen der Klasse “Vehicle”. Da der Proxy dann aber kein „Car“ ist, schlägt das instanceof fehl und das Auto wird nicht aufgetankt. Selbst wenn wir uns sicher sind, dass nur Autos in der Garage stehen (also auf das instanceof verzichten), schlägt spätestens der Cast nach „Car“ mit einer ClassCastException fehl.

Um sowohl auf das „instanceof“ wie auch auf den Cast verzeichten zu können, können wir das Visitor Pattern einsetzen.

public interface VehicleVisitor{
   void visit(Car car);
   void visit(Bicycle bicycle);
}

Für die Bequemlichkeit einen Adapter:

public VehilceVisitorAdapter implements VehicleVisitor{
   void visit(Car car){ /* leer */};
   void visit(Bicycle bicycle){/* leer */};
}

Unsere Fahrzeuge müssen den Visitor entgegennehmen:

public class Vehicle{
   public void accept(VehilceVisitor visitor){
       // leer
   }
...
}
 
public class Car extends Vehicle{
   public void reFuel(){};
   public void accept(VehilceVisitor visitor){
       visitor.visit(this);
   }
}

Jetzt kann die ursprüngliche Schleife angepasst werden:

VehicleVisitor fuelVisitor = new VehicleVisitorAdapter(){
     public void visit(Car car){
        car.reFuel();
     }
};
 
for(Vehicle v : garage.getVehicle()){
   v.accept(fuelVisitor);
}

Jetzt sind dann auch wirklich alle Autos aufgetankt und wir brauchen keine Angst mehr vor Hibernate Proxies haben. Nebenbei haben wir auf das „hässliche“ instanceof verzichtet und brauchen auch nicht mehr die Objekte zu casten.


Felix Breske


Eclipse Shortcuts Teil 2

Freitag, 08. Januar 2010

Eclipse verfügt über unzählige Shortcuts für nahezu alle Funktionen und Befehle. Jedes mal wenn ich auf der Suche nach neuen Shortcuts war, stieß ich auf lange unüberschaubare Listen. Es waren einfach zu viele neue Befehle um sich etwas zu merken. Neue Befehle habe ich fast nur durch abgucken gelernt. Auf diese Weise lerne ich nur einen neuen Shortcut, der mich dann auch gerade interessiert. Was bringen mir 100 Shortcuts, wenn ich vorher immer überlegen bzw. 2 mal probieren muss um den richtigen zu treffen. Da bleibe ich dann lieber “Mausschubser”.

Für diese Liste habe ich nun den Vormittag über jeden Shortcut, den ich benutzt habe in eine Datei getippt. Für diese Befehle brauche ich nicht nachdenken. Für alles andere verwende ich dann lieber die Maus.

STRG + d – Entfernen einer ganzen Zeile.

ALT + UP/DOWN – Verschieben der aktuellen Zeile nach oben oder unten.

ALT + LEFT/RIGHT – Springen zwischen den letzten bearbeiteten Zeilen.

STRG + 1 -Zeigt für die aktuelle Zeile die möglichen “quick fix” optionen an (z.b. Variable zuweisen, umbennen usw.). Für die meisten Optionen gibt es auch direkte Shortcuts aber die brauche ich mir so nicht merken. Nur durch diesen Shortcut ist diese Liste so kurz geblieben.

STRG + (SHIFT) + j – Suche im aktuellen Editor ohne Dialog. Diese Suche funktioniert ähnlich der Suche im Firefox. Nach dem drücken von STRG + J einfach das gesuchte Wort zu tippen. Im Editor wird dann nächsten möglichen Treffer gesprungen. Mit Shift wird die Suchrichtung geändert. Wiederholtes drücken spring zum nächsten möglichen Treffer.

STRG + SHIFT + t – Direktes öffnen einer Klasse. Neben ganzen Klassennamen können im Suchfeld auch nur die Großbuchstaben einer Klasse eingeben werden, wenn diese einen “Camel Case” Namen trägt (z.B. MTK für “MeineTestKlasse”, wenn es mehrere Treffer gibt dann funktioniert auch “MTKlas”).

STRG + SHIFT + r – Ähnlich der Suche einer Klasse, nur für beliebige Dateien.

STRG + SHIFT + g – “Java Search” für den markierten Code. Sehr hilfreich um Stellen zu finden, an denen eine Klasse verwendet oder eine Methode aufgerufen wird.

STRG + SPACE – Der Klassiker, kennt wohl wirklich jeder. Vervollständigung einer Methode, Klasse oder Variablen.

STRG + o – Zeigt innerhalb einer geöffneten Klasse alle darin befindlichen Methoden an, um dorthin springen zu können. Durch wiederholtes drücken können zusätzlich die geerbten Methoden angezeigt werden.

STRG + t – Zeigt auf einer Klasse die Vererbungshirarchie an, bzw. auf einer Methode die ggf. überschriebenen Methoden der Oberklassen an.

STRG + SHIFT + F – Formatieren der geöffneten Datei. Ist dank der Save Action nicht mehr wirklich notwendig. Ich brauche es nur noch für XML Dateien, da bei mir die Save Actions dort nicht ausgeführt werden.

STRG + 7 – Text aus/einkommentieren.

ALT + SHIFT + R – Umbennen einer Klasse Variablen oder Methode.

Speichern, also STRG + s braucht man ja eigentlich nicht mehr erwähnen. Bei vielen Entwicklern die ich kenne ist STRG + s ein Reflex und kein bewußter Befehl. Copy & Past benutze ich natürlich fast nie beim Entiwckeln ;-) .


Felix Breske


OutOfMemoryError? …. da geht noch was!

Freitag, 28. August 2009

Vor einigen Tagen kam hier im Büro die Frage auf, inwiefern eine Javaprogramm nach einem OutOfMemoryError noch funktioniert.

Das erste kleine Testprogramm war schnell geschrieben. Allerdings zeigte sich dabei, dass es gar nicht so leicht ist den Speicher “fast” voll zu machen um zu sehen was noch funktioniert. Alle StringBuffer, Listen, Vektoren usw. waren nicht geeignet. Diese Klassen arbeiten intern mit Arrays. Sobald ein Array voll ist, wird es durch ein neues mit der doppelten Größe ersetzt. Dabei wird kurzzeitig der doppelte Speicherplatz benötigt. Es ist also wahrscheinlich das der OutOfMemoryError bei genau diesem Vergrößerungsvorgang erzeugt wird. Danach wird das neue doppelt so große Array aber vom GarbageCollector(GC) weggeräumt. Dadurch wird Speicher freigegeben und es kann erstmal normal weitergearbeitet werden (es sei denn man versucht in die Liste nochmal etwas einzufügen, also eine erneute Verdoppelung).

Um dieses Problem zu umgehen, habe ich eine verkette Liste verwendet. Hier kann ich auf alle Elemente eine Referenz halten (damit der GC nicht aufräumt) und brauche kein Array und keine Art von Listenimplementation.

In dem Testprogramm selbst wird nun in zwei Ebenen den Fehler abgefangen. In der ersten Ebene wird noch eine Referenz auf das erste Element in der Liste gehalten. Der GC hat also keine Möglichkeit aufzuräumen bzw. Speicher freizugeben. Je nachdem wann der GC zuletzt gelaufen ist, können noch einige Zeilen in dem catch Block ausgeführt werden. Teilweise reicht schon die Ausgabe des StackTrace um einen erneuten OutOf MemoryError zu erzeugen.

Wird nach dem ersten OutOfMemoryError ein weitere Error erzeugt (in dem catch Block), so wird dieser von der zweiten Ebene abgefangen. Hier wird dann keine Referenz auf das erste Element mehr gehalten. So kann der GC den ganzen Speicher freiräumen und das Programm kann ein zweites mal ohne Einschränkungen gestartet werden. Die Anzahl der erzeugten Objekte in der Liste nimmt dabei nur vom ersten zum zweiten Durchlauf ab. Danach bleibt die Zahl konstant. Es wird also wirklich der komplette Speicher geleert.

import java.io.IOException;
 
public class Test {
    public static void main(final String[] args) throws IOException {
        while (true) {
            try {
                final ListItem firstElement = new ListItem();
                ListItem lastElement = firstElement;
                final Runtime r = Runtime.getRuntime();
                int i = 0;
                try {
                    while (true) {
                        while (true) {
                            System.out.println(countElemtns(firstElement));
                            System.out.println(i++ + " Used Memory: " + (r.totalMemory() - r.freeMemory()) / (1024 * 1024));
                            lastElement = addItem(lastElement, i);
                        }
                    }
                } catch (final Throwable t) {
                    // An dieser Stelle reicht 1 Zeichen mehr im buffer um einen OutOfMemoryError zu erzeugen
                    // final String test = new String("Gefangen"); // das geht trotzdem
                    // System.out.println(test); // das auch
                    t.printStackTrace(); // manchmal reicht das schon zum kaputt machen
                    lastElement = addItem(lastElement, i++);// Unterschiedlich wie oft wir das brauchen um echt einen weiteren OutOfMemory zu erzeugen
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    System.out.println(countElemtns(firstElement));
                    System.out.println("Wird meist nicht erreicht");
                    System.out.print("Neustart mit Enter: ");
                    System.in.read();
                }
            } catch (final Throwable e) {
                e.printStackTrace();
                System.out.print("Neustart mit Enter: "); // Keine Referenz mehr auf 'buffer' daher neustart mit leerem speicher moeglich
                System.in.read();
            }
        }
    }
 
    static ListItem addItem(ListItem item, final int i) {
        final ListItem newItem = new ListItem();
        newItem.s = "TEST" + i;
        item.child = newItem;
        item = newItem;
        return item;
 
    }
 
    static int countElemtns(ListItem item) {
        int i = 0;
        while (true) {
            if (item != null) {
                i++;
                item = item.child;
            } else {
                break;
            }
        }
        return i;
    }
 
    static class ListItem {
        ListItem child;
        String s;
    }
 
}

Um die Laufzeit nicht unnötig lang zu machen sollte der Speicher beim starten des Programms möglichst klein sein:

java -Xmx2m -Xms2m Test

Es ist also tatsächlich möglich nach einem OutOfMemoryError weiterzuarbeiten. Hierfür muss nur sichergestellt werden, dass nach dem entstehen des Errors sofort Speicher freigegeben wir. Jede weitere Zeile Code kann sonst zum nächsten Fehler führen.

Interessant wäre noch zu wissen ob es möglich ist den Speicher soweit zu füllen, dass bereits der Aufruf des GC zu einem weiteren Error führt. Viel Speicher kann er nicht benötigen wenn bereits das printStackTrace zum nächsten Fehler führt. Ggf braucht er gar keinen zusätzlichen Speicher bei der Ausführung, sondern hat schon alle im Speicher nach dem ersten Start?

In dem Programm habe ich wohl gegen alle Regeln des sauberen Programmierens verstoßen. Als Programmieren sollte man nie in die Situation kommen mit Throwable bzw. Error Klassen zu arbeiten. Im Fall eines OutOfMemoryErrors kann man wie oben beschrieben tatsächlich weiterarbeiten wenn genug Speicher freigegeben wurde, aber z.B. ein Error durch einen Festplattendefekt wird wohl kaum durch den GC behoben (oder kann der GC Festplatten reparieren? ).

Nach der Entwicklung eines solchen Codes bitte das Händewaschen nicht vergessen.


Felix Breske