Performance

Das Java Memory Modell (1. Akt)

Eine der großen Stärken der Java Plattform ist die automatische Speicherverwaltung. Jeder der schon mal mit C/C++ programmiert hat und sich um die Allokation und Freigabe von Speicher selber kümmern musste, weiß diese Eigenschaft der Java Runtime zu schätzen. Die in der Praxis häufig aufgetretenen Probleme, dass Speicher zu früh  (corrupted pointer) oder unvollständig (memory leak) freigegeben wurden sind damit grundsätzlich behoben. Die Frage ist: Warum schreibe ich dann überhaupt diese Blog Einträge?

Leider kann auch eine implizite Speicherverwaltung, wie Java sie hat, nicht verhindern, dass man Anwendungen programmiert, die zu Speicher Problemen führen, obwohl eine manuelle Allokation in Java nicht möglich und per Spezifikation ausgeschlossen ist. Das Resultat solcher Fehler ist dann in der Regel eine Ausnahme vom Typ: java.lang.OutOfMemoryError.

(weiterlesen…)

Mirko Novakovic

 

Java OutOfMemoryError – Eine Tragödie in sieben Akten

Jeder Java Programmierer wird früher oder später über folgende Ausnahme stolpern: java.lang.OutOfMemoryError.

Mit ein wenig Glück passiert das noch während der Entwicklung. Tritt diese Ausnahme aber im produktiven Betrieb auf, erhält man schnell eine ungewollte Aufmerksamkeit im Management, da ein Java OutOfMemoryError in der Regel gleichbedeutend mit einem Produktionsausfall ist. Die Ursachen für diesen Fehler in der JVM sind vielfältig und nicht immer einfach zu analysieren. In der Praxis sehe ich täglich, dass Server jede Nacht neu gestartet werden um kontinuierlich anwachsendem Speicher Herr zu werden oder aber Anwendungen regelmäßig aus vermeindlich unerklärlichen Gründen ausfallen, auch wenn ein java.lang.OutOfMemoryError im Log darauf hin weist, dass der Hunger der Anwendung nach mehr Speicher einfach zu groß war.

(weiterlesen…)

Mirko Novakovic

 

Profiling von PHP Anwendungen

Wir bei codecentric haben nicht nur den Anspruch führend im Bereich Java Performance zu sein, sondern auch auf anderen Gebieten unseren Kunden mit Rat und Tat zur Seite zu stehen.

In einem früheren Eintrag habe ich schon einmal über einen Vergleich von PHP und Java geschrieben. Dabei habe ich unter anderem Tooling Probleme bei PHP bemängelt. In diesem Eintrag möchte ich deshalb darauf eingehen wie man PHP Anwendungen Profilen kann.

Für Java Entwickler sollte JProfiler ein Begriff sein. Das Äquivalent auf PHP Seite nennt sich Xdebug + CacheGrind. Xdebug ist dabei das Mittel um Skriptaufrufe zu profilen, WinCacheGrind nutzen wir dann anschließend um die gesammelten Daten auszuwerten. In diesem Beispiel profilen wir einen Kommandozeilen Aufruf des symfony Framworks. Da ich dort auch commiter bin nutze ich Xdebug dort häufiger um eine gute nichtfunktionale Qualität sicherzustellen.

Zuerst lädt man die ‘.dll’ (oder ‘.so’ für Linux) Extension von Xdebug herunter und legt sie zu den weiteren Extensions in das ‘php/ext’ Verzeichnis. Um die Extension zu aktivieren muss man in der ‘php.ini’ Xdebug einbinden und mitteilen wo es hin profilen soll:

zend_extension_ts="d:\dev\php\ext\php_xdebug-2.0.3-5.2.5.dll"
xdebug.profiler_output_dir="c:\xdebug-profiling"
xdebug.profiler_enable=1

Es ist dabei darauf zu achten, daß diese Einstellungen in den richtigen ‘php.ini’ Dateien gemacht werden. im PHP Verzeichnis findet sich die für die Kommandozeile, im Apache Verzeichnis üblicherweise die für den von Apache gestarteten PHP Prozess. Auch muss das angegebene Zielverzeichnis existieren, ansonsten werden keine Dateien abgelegt.

Als kurzer Test sollte ein Aufruf von ‘php -m’ folgendes liefern:

[Zend Modules]
Xdebug

Als nächstes profilen wir einen Aufruf. Z.B. einen der symfony sandbox, zum generieren einer Anwendung:

php symfony generate:app codecentric

In diesem Fall entsteht eine etwa 8MB große ‘cachegrind.out.4896′. Dieses kann nun mit WinCacheGrind geladen werden. Dies sieht dann in etwa so aus:

Leider bietet WinCacheGrind nur begrenzte Funktionalität. KCacheGrind ist da schon etwas weiter, jedoch bei weitem noch nicht so ausgereift wie die häufig von uns eingesetzten Java Tools. Außerdem findet sich keine Memory Ansicht. Dies ist wohl auch darauf zurückzuführen, daß das Memory Management in PHP sehr dürftig ist. Brauchbar ist dagegen die Anzeige der benötigten Zeit.

Auf dem Tab ‘Overall’ findet sich der Code der in Summe die meiste Zeit verbraucht hat. Dies ist für gewöhnlich auch die Stelle an der man schnell die größten Gewinne machen kann. Für den Symfony Task findet sich beispielsweise hier:

Function  : sfFinder->search_in
Avg. Self : 0,2ms
Avg. Cum. : 5,1ms
Total Self: 127ms
Total Cum.: 3214ms
Calls     : 629

die search_in Funktion benötigt selbst im Schnitt 0,2ms, aufgerufene Funktionen benötigen in Summe im Schnitt 5,1ms. Da die Funktion 629 mal aufgerufen wurde ergibt dies dann die entsprechenden Gesamtsummen. Nun kann man entweder schauen ob die Funktion wirklich so oft aufgerufen werden muss, und wenn ja, ob man nicht einen einzigen Durchlauf noch optmieren kann.

Auch ohne die richtige Spürnase für Performanceprobleme kann man mit dem beschriebenen Tooling auch PHP Anwendungen in der Performance optimieren. Es bleibt abzuwarten ob sich PHP und die entsprechenden Tools soweit entwickeln werden wie es heute schon status quo in Java ist.

Fabian Lange

 

ThreadLocal Memory Leak

Da ich schon häufiger auf Memory Leaks gestoßen bin, die durch ThreadLocal Variablen auftreten, möchte ich dieses Phänomen genauer beleuchten.

ThreadLocal Variablen werden in Java benutzt, um Variablen an einen Thread zu binden – jeder Thread bekommt eine eigene, unabhängige Instanz dieser Variable zugewiesen. Normalerweise werden diese Variablen genutzt, um Status Informationen innerhalb eines Threads zu halten – z.B. die Benutzerkennung oder einen Mandanten.

Der Lebenszyklus einer ThreadLocal Instanz ist eng gekoppelt an den Lebenszyklus des entsprechenden Threads. Wird der Thread beendet und durch den GarbageCollector entfernt, sind auch die damit assoziierten ThreadLocal Variablen Kandidaten für den GarbageCollector.

Die Memory Probleme treten dann auf, wenn ThreadLocal Variablen innerhalb eines Application Servers verwendet werden. Application Server verwalten Thread in eigenen Thread Pools, um Ressourcen einzusparen und die Performance zu optimieren (Beispiel der Einstellungen für Tomcat 6 Http Conncector). Wird z.B. ein HttpServletRequest an die ServletEngine des Application Servers gesendet, wird für die Abarbeitung des Servlets ein freier, so genannten Worker-Thread aus dem entsprechenden Servlet ThreadPool entnommen. Dieser Thread arbeitet dann die Programmlogik des Servlets ab. Wird im Servlet oder der aus dem Servlet aufgerufenen Java Klassen eine ThreadLocal Variable genutzt, werden diese mit dem aktuellen Thread assoziiert. Nachdem das Servlet vom Thread abgearbeitet und die Response geschrieben wurde, wird der Thread aber nicht beendet und vom Garbage Collector aufgeräumt, sondern wieder in den ThreadPool zurückgegeben, so dass mit dem selben Thread einen neue Anfrage abgearbeitet werden kann. Die ThreadLocal Variablen bleiben also erhalten.

Je nach Größe des ThreadPools (in produktiven Systemen können das mehrere Hundert Threads sein) und der Größe der Objekte in der ThreadLocal Variable, kann das zu Problemen führen. Sind beispielsweise 200 Threads im ThreadPool aktiv und die ThreadLocal Varibale ist 5MB groß, belegen diese Variablen im schlimmsten Fall bereits 1GB Heap Speicher. Dies führt schnell zu erhöhter GC Aktivität und je nach Heap Größe zu einem Abbruch der JVM.

In einem konkreten Fall musste ein Server täglich neu gestartet werden, weil es zu einem OutOfMemoryError gekommen ist. Um das Prolem zu analysieren wurde zur Laufzeit ein Heapdump erzeugt. (siehe dazu den Blog Eintrag zur Erzeugung von Heapdumps) Die Analyse des Dumps mit Eclipse MAT hat ziemlich schnell gezeigt, dass es sich um ein ThreadLocal Problem handelt.

Der obige Screenshot zeigt den Dominator Tree des Dumps. Markiert sind 6 Threads die jeweils ca. 14MB Speicher referenzieren.

Der nächste Screenshot zeigt die Details eines Threads. Der hohe Speicherverbrauch ist auf eine ThreadLocal Variable zurückzuführen, die eine DOMParser inkl. geparstem Dokument referenziert – insgesamt mehr als 14MB groß. Da MAT auch die Inhalte der Objekte anzeigt, konnte an Hand des XML Inhalts identifiziert werden, dass die Dokumente zu einem WebService Aufruf gehören. Die Zugriffsklassen wurden mit JBoss WS generiert. Eine Suche in Google führte schnell zu einem Bug in der eingesetzten jbossws Version 1.2.0, der in der Version 1.2.1 gefixt wurde: “DOMUtils doesn’t clear thread locals”.

Das Beispiel zeigt, dass man vorsichtig sein muss, wenn man ThreadLocal Variablen innerhalb eines ApplicationServer verwendet (vor allem wenn man Application Server Hersteller ist). Es ist wichtig dafür zu sorgen, dass der Inhalt der Variablen gelöscht wird, bevor der Thread wieder in den ThreadPool zurückgestellt wird. Die Daten sind auch fachlich gefährlich, wenn Sie nicht gelöscht werden, da nicht vorhergesagt werden kann, welche Anfrage der Thread als nächstes bearbeitet.

In einigen Projekten in denen wir ThreadLocal Variablen nutzen, haben wir einen ServletFilter geschrieben, der dafür sorgt, dass die Daten bereinigt werden.

Mirko Novakovic

 

Memory Analyse Teil 1 – Java Heapdumps erzeugen

Bei Troubleshooting Einsätzen ist der Java Heapdump eines der wichtigsten Hilfsmittel, um Memory Leaks oder hohen Speicherverbrauch zu analysieren. Der Vorteil von Heapdumps ist, dass man sie auch in produktiven Umgebungen erzeugen kann  – also da wo die Probleme am häufigsten auftreten. Alle aktuellen Java Virtual Maschines unterstützen die Erzeugung von Heapdumps, ohne zusätzliche Tools installieren zu müssen.

In dieser Blog Serie möchte ich zeigen, wie man Memory Probleme analysieren und beheben kann. Dazu werden Antipatterns und unterschiedliche Memory Probleme beschrieben und Lösungsszenarien aufgezeigt.

Der erste Teil zu diesem Thema beschäftigt sich mit der elementaren Aufgabe einen Heapdump zu erzeugen – die wichtigste Voraussetzung für eine erfolgreiche Analyse.  Die unterschiedlichen Hersteller von JVMs (Sun, IBM, BEA) haben unterschiedliche Werkzeuge und Dump Formate in die JVMs integriert – dieser Blog wird die Implementierung von Sun betrachten und am Ende Hinweise für andere Implementierungen geben.

Die Sun Java Virtual Maschine enthält mehere Tools und Optionen, um einen Heapdump zu erstellen:

- Automatisch bei einem java.lang.OutOfMemoryError

- Über das Kommandozeilen Tool jmap

- Über ein bereitgestelltes MBean (Java Management Extension – JMX) und das Tool jconsole

Natürlich gibt es auch die Möglichkeit über das Java Virtual Maschine Tool Interface (JVMTI) einen Dump zu erzeugen – allerdings muss man hierfür einen Agenten in C erstellen. Viele Profiler (JProfiler, dynaTrace Diagnostics) bieten aber die Möglichkeit eine Dump über JVMTI zu erzeugen und die Daten grafisch auszuwerten.

Um automatisch einen Heapdump zu erzeugen, wenn ein OutOfMemoryError geworfen wird, muss man einen JVM Kommandozeilen Parameter beim Start von java mitgeben:

-XX:+HeapDumpOnOutOfMemoryError

Der Parameter bewirkt, dass die JVM bei einem OutOfMemoryError einen Headump im HPROF Format in das aktuelle Verzeichnis schreibt. Der Name des Dumps entspricht der Konvention java_pid.hprof.  Um das Verzeichnis und den Namen der Datei selber zu bestimmen, kann man zusätzlich den Parameter -XX:HeapDumpPath=path_to_file definieren.

Die automatische Erzeugung von Dumps über Parameter ist allerdings nicht immer sinnvoll. In manchen Situation möchte man den Dump auch im laufenden Betrieb erzeugen. Für diesen Fall liefert Java ab Version 1.4.2_09, 1.5.x und 1.6.x das Tool jmap mit. Einen Heapdump im HPROF Format erzeugt man mit durch den Kommandozeilen Befehl jmap -dump:file=path_to_file java_process_id. Die Java Prozess Id legt fest von welcher aktiven, lokalen JVM der Headump angefordert werden soll. Die Prozess Id kann man mit dem JVM Tool jps ermitteln. (Hinweis: Das Tool jmap ist allerdings nicht jeder Plattform und JVM Version verfügbar) Alternativ kann man auch den JVM Parameter -XX:+HeapDumpOnCtrlBreak nutzen und dem Prozess ein SIGQUIT Signal (kill -3 bei Unix und Ctrl-Break bei Windows) senden.

Mit Java 6 hat Sun zusätzlich ein JMX MBean eingeführt, das Methoden zum Erzeugen eines Heapdumps enthält. Um einen Dump über JMX zu erzeugen startet man die integrierte JMX Konsole mit dem Befehl jconsole und verbindet sich mit der entsprechenden JVM. Lokal ist dafür keine weitere Konfiguration der JVM notwendig – möchte man auf eine JVM auf einem anderen Rechner zugreifen, muss JMX entsprechend konfiguriert sein.

Unter der Lasche MBean findet man einen Explorer mit den verfügbaren MBean innerhalb der JVM. Das MBean com.sun.management.HotSpotDiagnostic enthält die gesuchte Methode dumpHeap(String, boolean). Mit Hilfe des ersten String Parameters kann der Pfad und der Name des Heapdumps angegeben werden. Der Screenshot zeigt die entsprechende Ansicht. Drückt man auf den dumpHeap-Button wird ein Memory-Dump der JVM ausgeführt.

Der nächste Teil dieser Serie wird sich mit der Auswertung und dem Aufbau eines Heapdumps beschäftigen und erste Tips zur Analyse von Memory Problemen geben.

Informationen wie man Heapdumps mit der IBM JVM erzeugt finden sich in der IBM JVM Diagnosis Documentation.

Information zu BEA JRockit und dem JRockit Memory Leak Detector finde sich in der JRockit Dokumentation.

Mirko Novakovic

 

Seiteneffekte beim Connection-Sharing

Heute hatten Christian und ich es mit einem sehr mysteriösen Connection Problem zu tun. Als Teil einer Prozesskette schlug ein Rest WebService Aufruf fehl. Jedoch tat er dies nur unter Last. Auf Grund dessen wollten wir das Laufzeitverhalten auch nicht durch Debugging Breakpoints zu sehr beeinflussen und beschränkten uns auf Debug-Logausgaben. Dabei war dem nicht ganz einfach beizukommen: Der WebService Client ist eine Erweiterung des Apache Commons HTTPclient. Der Webservice in Struts2 mittels des Restful2ActionMappers implementiert. Beide bekamen über Spring diverse Dienste injected. Zuerst hatten wir die ungewöhnlich große Größe der Post Requests und der Responses in Verdacht, jedoch schienen diese in Ordnung zu sein. Trotzdem häuften sich Variationen von:

java.io.IOException: CRLF expected at end of chunk: 72/84
java.io.IOException: Bad chunk size: somexml

Als uns klar wurde, daß das Problem auf dem Transportweg auftritt sahen wir uns um und entdeckten diese Spring Konfiguration:

<bean name="restClient" class="de.codecentric.framework.RestClient">
  <property name="httpClient">
    <bean class="org.apache.commons.httpclient.HttpClient" />
  </property>
</bean>

Standardmäßig hat der HttpClient einen ConnectionManager der immer die gleiche Connection herausgibt. Sogar an unterschiedliche Threads. Wenn man das einmal weiß, findet man schnell den

org.apache.commons.httpclient.MultiThreadedHttpConnectionManager

doch leider half dieser auch nicht weiter. Es gesellte sich eine

java.io.IOException: connection closed

hinzu. Was vielleicht überraschend klingt: der MultiThreadedHttpConnectionManager erlaubt per default nur 2 Verbindungen zum gleichen Host. Nachdem wir diese erhöhten lief die Anwendung auch wieder unter Last.

So ungefähr sieht dann die Spring Konfiguration aus:

<bean name="restClient" class="de.codecentric.framework.RestClient">
  <property name="httpClient">
    <bean class="org.apache.commons.httpclient.HttpClient">
      <property name="httpConnectionManager">
        <bean class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager" destroy-method="shutdown">
          <property name="params">
            <bean class="org.apache.commons.httpclient.params.HttpConnectionManagerParams">
              <property name="defaultMaxConnectionsPerHost" value="20" />
            </bean>
          </property>
        </bean>
      </property>
    </bean>
  </property>
</bean>

Fazit: Wenn sich verschiedene Threads die gleiche UrlConnection teilen geschehen die wundersamsten Dinge (Exceptions) :-)

Fabian Lange

 

Nächste Seite »