Kategorien
Allgemein

Mit angezogener Handbremse

Seit einigen Wochen hatten wir ein Problem mit unserem Softwareloadbalancer, der auf einem unserer Tomcatserver läuft. Dachten wir.

Die durchschnittliche Last (loadavg) auf dem Server ging in relativ regelmäßigen zweistündlichen Abständen auf über zehn. Da die Lastprobleme mit dem Lastverteiler  umzogen hatten wir den nebenher laufenden Tomcat relativ früh als Verursacher der Probleme ausgeschlossen.

Ein Irrtum.

Wegen eines anderen Problemes haben wir während einer solchen hochlast Phase den Tomcat mittels

kill -quit $TOMCAT_PID

mehrere Threaddumps schreiben lassen. Bei der Analyse dieser Dumps fiel uns dann auf, dass ein übervorsichtiger Programmierer eine Garbage Collection bei jedem Aufruf seines Servlets veranlasste.

Glücklicherweise kann mittels Parameter der JavaVM das Beachten dieser Aufforderung ausgetrieben werden. Seit wir die Option -XX:+DisableExplicitGC für die Tomcat JavaVM nutzen, sind alle Lastprobleme verschwunden. Ebenso zeigte sich, dass die Anzahl der möglichen Anfragen an das Servlet von etwa vier Anfragen pro Sekunde auf etwa 200 Anfragen pro Sekunde hochschnellten.

Und netterweise sind in dem Servlet auch die Aufrufe von System.gc() verschwunden.

Kategorien
Allgemein

Immer eins nach dem anderen

Ein kleines Beispiel zeigt, wo die Reihenfolge der Deklaration von Variablen in Java Klassen wichtig ist.

Auf der Tomcat Mailingliste kam heute eine Mail, in der jemand fragte, warum sein Singleton im Tomcat nicht einamlig in der JVM vorhanden war. Die Lösung war wohl, dass er zwei verschiedene Classloader benutzte und daher zwei verschiedene Instanzen.

Ein anderes Problem seines Codes war aber ganz anderer Natur. Der Code sah etwa so aus:

public class Singleton {
    private static Singleton instance = new Singleton();
    private static int counter = 0;

    private Singleton() {
         System.out.println("Counter: " + (++counter));
    }

    public static getInstance() {
        return instance;
    }

    public int getCounter() {
        return counter;
    }
}

Auf den ersten Blick sieht das in Ordnung aus. Der Konstruktor gibt brav ein "Counter: 1" aus und man ahnt nichts böses. Bis dann ein kleiner Testfall mit jUnit aufkreuzt:

public class SingletonTest extends TestCase {
     public void testSingletonCounter() {
          Singleton instance = Singleton.getInstance();
          assertEquals(instance.getCounter(), 1,
              "Es kann nur einen geben");
     }
}

Hier wird dann plötzlich klar, dass intern der counter trotz anderweitiger Ausgabe wieder auf 0 gesetzt wurde. Das liegt daran, dass die Initialisierung der statischen Klassenvariablen der Reihe nach geschieht. Im Konstruktor ist counter also erstmal nicht definiert – implizit also null – und wird dann auf eins inkrementiert. Nach dem Konstruktor wird dann die vermeintlich noch nicht initialsierte Variable auf 0 gesetzt.

Das kann ganz schön ins Auge gehen.