Angelika Langer - Training & Consulting
HOME | COURSES | TALKS | ARTICLES | GENERICS | LAMBDAS | IOSTREAMS | ABOUT | NEWSLETTER | CONTACT | Twitter | Lanyrd | Linkedin
 
HOME 

  OVERVIEW

  BY TOPIC
    JAVA
    C++

  BY COLUMN
    EFFECTIVE JAVA
    EFFECTIVE STDLIB

  BY MAGAZINE
    JAVA MAGAZIN
    JAVA SPEKTRUM
    JAVA WORLD
    JAVA SOLUTIONS
    JAVA PRO
    C++ REPORT
    CUJ
    OTHER
 

GENERICS 
LAMBDAS 
IOSTREAMS 
ABOUT 
NEWSLETTER 
CONTACT 
Java Performance - Performance Profiling Tools

Java Performance - Performance Profiling Tools
Java Performance: Profiling

Wie funktionieren Profiler-Tools?

JavaSPEKTRUM, November 2005
Klaus Kreft & Angelika Langer

Dies ist das Manuskript eines Artikels, der im Rahmen einer Kolumne mit dem Titel "Effective Java" im JavaSPEKTRUM erschienen ist.  Die übrigen Artikel dieser Serie sind ebenfalls verfügbar ( click here ).

 

Im vorangegangenen Beitrag dieser Kolumne haben wir uns mit Micro-Benchmarking beschäftigt. Micro-Benchmarks sind Vergleichsmessungen, bei denen die Performance verschiedener, alternativer Algorithmen gemessen und anschließend verglichen wird, um den besseren (d.h. schnelleren) Algorithmus zu bestimmen.  Dieses Mal wollen wir uns mit dem Profiling von Java-Anwendungen beschäftigen.  Auch beim Profiling geht es um Messungen, die u.a. Aussagen über die Performance eines Programms liefern.  Beim Profiling wird, anders als beim Micro-Benchmarking, die gesamte Anwendung gemessen und analysiert.  Solche Messungen werden während der Entwicklung vorgenommen, um Schwachstellen im Programm wie zum Beispiel Performance-Bottlenecks zu identifizieren und durch ein anschließendes Tuning zu beseitigen.  In diesem und den nächsten Beiträgen sehen wir uns an, wie ein Profiling gemacht wird. Da zu diesem Zweck in der Regel entsprechende Profiling-Tools verwendet werden, betrachten wir in diesem Artikel die Architektur dieser Werkzeuge und erläutern, welche Art von Analysen man damit machen kann.  In den daran anschließenden Beiträgen diskutieren wir, wie man mit diesen Tools verschiedene Profilings macht und wie man dabei am geschicktesten vorgeht.
 

Was ist Profiling?

Profiling wird oft mit Benchmarking und Monitoring in einen Topf geworfen.  Deshalb wollen wir zunächst einmal eine Abgrenzung vornehmen und klären, um was es beim Profiling eigentlich geht.

Beim Benchmarking geht es um den Vergleich von zwei oder mehr Alternativen. Typischerweise wird dabei die Performance verglichen; es kann aber auch um Speicherverbrauch oder andere Eigenschaften gehen. Wir haben im letzten Beitrag dieser Kolumne über Micro-Benchmarking in Hinblick auf Performance gesprochen und dabei gesehen, dass es beim Micro-Benchmarking um den Vergleich von zwei Algorithmen geht, also um einen kleinen Ausschnitt der Realität.  Beim Macro-Benchmark vergleicht man größere Einheiten, zum Beispiel ganze Frameworks („ein Web-Server verglichen mit einem anderen Web-Server“, oder „eine JVM verglichen mit einer anderen JVM“).  Benchmarking ist eine Tätigkeit, die während der Entwicklung stattfindet, zum Beispiel um den schnellsten Algorithmus zu ermitteln, oder auch schon vor der Entwicklung, um den besten Framework für das Projekt zu bestimmen.

Beim Profiling geht es darum, eine Anwendung zu untersuchen und darin Schwachstellen zu bestimmen.  Das ist ebenfalls eine Tätigkeit, die während der Entwicklung vorkommt.  Beispielsweise kann das Ziel eines Profiling das Aufspüren von Performance-Bottenecks oder eines Memory-Leaks sein, die anschließend beseitigt werden sollen.  Beim Profiling geht es, anders als beim Benchmarking, nicht um einen Vergleich, sondern um das Auffinden von Schwachstellen, in der Regel mit dem Ziel, diese Schwachstellen anschließend zu beseitigen.

Das Monitoring ist eine Untersuchung, die nicht während der Entwicklung, sondern später am fertigen System vorgenommen wird.  Dabei geht es um das Auffinden von Auffälligkeiten.  Ein Monitor würde beispielsweise von einem Systemadministrator verwendet, um zu überprüfen, ob die CPU-Ausnutzung für längere Zeit bei 100% ist.  Auf diese Weise können zum Beispiel Lastprobleme festgestellt werden.

Dem Monitoring und Profiling liegt die gemeinsame Idee zugrunde, dass Daten über das laufende System erhoben werden, die eine Analyse zum Auffinden von Schwachstellen im System zulassen, anders als beim Benchmarking, bei dem Daten über mehrere Alternativen erhoben werden zum Zwecke des Vergleichs.

Der Übergang zwischen Profiling und Monitoring ist fließend. Zwar wird das Profiling in der Regel während der Entwicklung gemacht und das Monitoring danach im fertigen System.    Monitoring und Profiling unterscheiden sich durch ihre Zielsetzung und damit auch durch ihre Vorgehensweise. Bei einem Monitoring möchte man das laufende System nicht nennenswert durch die Datenerhebung belasten, was man hingegen beim Profiling durchaus akzeptiert.  Naturgemäß werden dadurch beim Profiling mehr und genauere Daten über den Ablauf der Anwendung erhoben als beim weniger aufwändigen Monitoring. Üblicherweise liefern die Daten beim Monitoring nur relativ allgemeine Erkenntnisse im Vergleich zum Profiling. Zum Beispiel könnte ein Monitoring ergeben, dass eine bestimmte JVM die CPU-Ausnutzung zu 100% ausnutzt, ohne aber zu sagen, welche Methoden innerhalb der JVM den CPU-Verbrauch verursachen. Diese Details bekäme man bei einem Profiling heraus. Entsprechend sind für die Analyse der Daten beim Profiling detailliertere Kenntnisse des Systems erforderlich als bei einem Monitoring; das Profiling wird deshalb typischerweise vom Entwickler gemacht und das Monitoring vom Systemadministrator.

Wir werden uns im Folgenden dem Profiling widmen, mit dem Ziel Performance-Engpässe zu identifizieren. Dazu müssen möglichst detaillierte und genaue Daten über den Ablauf des Programms erhoben und analysiert werden. Die Daten können aus unterschiedlichen Quellen stammen und können unterschiedlich für die Analyse aufbereitet werden.  In der Regel macht man das nicht alles „zu Fuß“, sondern verwendet entsprechende Profiler-Tools.  In der Java-Welt arbeiten diese Tools bei der Erzeugung der Profiling-Daten eng mit der JVM zusammen. Diese erzeugten Daten liefern z.B. die Information, wie häufig eine Methode während des Testlaufsaufgerufen wurde, wieviel CPU-Zeit dafür benötigt wurde, wieviel Objekte (von welchem Typ) dabei erzeugt wurden und vieles mehr. Sehen wir uns also in diesem Beitrag zunächst die Tools genauer an, ehe wir in nachfolgenden Beiträgen die Strategien für die Erhebung und die Analyse der Daten besprechen.

Profiler-Tools im Allgemeinen

Warum sollte man sich für die Funktionsweise von Profiling-Tools interessieren?

Um ein erfolgreiches Profiling zu machen, ist es zunächst einmal nicht  zwingend erforderlich, sich mit der Architektur und Funktionsweise der eingesetzten Tools auszukennen.  Die Profiler-Tools kann man bedienen, ohne irgendetwas über deren Funktionsweise zu wissen. Allerdings sind Kenntnisse über die Arbeitsweise der Tools recht hilfreich, um die gewonnenen Daten zu bewerten und zu analysieren.  Wenn man weiß, wie die Profiling-Daten erhoben werden und aus welcher Quelle sie stammen, dann hilft das bei der korrekten Interpretation der Daten.  Das Profiling selbst kann nämlich je nach Art der Datenerhebung die Performance der gemessenen Applikation unterschiedlich stark beeinflussen.  Wenn wir nach Performance-Bottlenecks suchen, dann ist es günstig, wenn wir wissen, dass die Datenerhebung selbst bereits die Performance beeinflusst und wie stark dieser Einfluss ist.  Ohne Kenntnis über die Arbeitsweise der Profiler-Tools ist eine Bewertung der Messergebnis recht schwierig und fehleranfällig.  Also sehen wir uns die Profiler-Tools einmal genauer an.

Was messen Profiler?

Sehen wir uns als erstes an, was Profiler-Tools eigentlich messen.  Es gibt nämlich verschiedene Arten von Profilern.  Das ist nicht unbedingt offensichtlich, weil die verschiedenen Profiler oft in einem einzigen Tool zusammengefasst sind.  Neben solchen kombinierten Tools gibt es auch spezielle Tools für spezielle Profilings.

Profiler-Tools konzentrieren sich auf folgende Aspekte:

  • Time Profiler. Diese Tools messen die Zeit, die eine Applikation in jeder Methode verbringt.  Damit können Methoden identifiziert werden, die besonders viel Zeit in Anspruch nehmen.  Das ist interessant für ein Performance-Profiling.  Die Methoden, die auffallend viel Zeit verbrauchen, sind nämlich die Performance-Bottlenecks in der Anwendung, die man gerne beseitigen möchte.
  • Space Profiler. Diese Tools verfolgen, wie sich der Heap entwickelt und welche Methoden wie viel Heap-Speicher anfordern.  Damit lassen sich Methoden identifizieren, die besonders viel Speicher verbrauchen.  Auch das ist interessant fürs Performance-Profiling, weil „viel Speicherverbrauch“ häufig auch „viel Garbage Collection“ bedeutet, und damit auch „viel Performance-Verlust“. Space Profiler helfen auch beim Aufspüren von sogenannten Memory Leaks.  Memory Leaks sind Objekte, die noch erreichbar sind und Heap-Speicher beanspruchen, obwohl sie eigentlich nicht mehr gebraucht werden.  Memory Leaks sind in erster Linie Fehler und keine Performance-Bottlenecks.
  • Thread Profiler. Diese Tools helfen bei der Analyse von Synchronisierungsproblemen.  Sie analysieren zum Beispiel, welche Threads auf welche Locks warten und welche Threads diese Lock gesperrt haben.  Damit lassen sich Deadlocks aufspüren, wenn sie reproduzierbar sind.  Deadlock-Detection und die Analyse des Synchronisationsverhaltens ist für das Performance-Profiling nur bedingt von Interesse.  Allerdings wird es interessant, wenn man feststellt, dass einige Threads die meiste Zeit mit Warten statt mit produktiver Arbeit verbringen.  Dies kann zum Beispiel ein Indiz dafür sein, dass die Verteilung von Tätigkeiten auf unterschiedliche Threads nicht optimal gelöst wurde.
Wir werden später alle drei Arten von Profilern einsetzen.  Am naheliegendsten ist der Einsatz eines Time-Profilers, wenn man an Performance-Verbesserungen arbeitet.  Aber exzessiver Speicherverbrauch und übermäßige Warten sind ebenfalls Effekte, die sich negativ auf die Performance auswirken und untersucht werden sollten.
 

Wann liefern Profiler ihre Daten?

Profiler beschaffen während der Programmausführung Daten über den Programmablauf und machen diese Daten für die Analyse zugänglich.  Der Zeitpunkt, zu dem die erhobenen Daten vom Profiler für die Analyse zur Verfügung gestellt werden, ist unterschiedlich. Man unterscheidet zwischen Post-Mortem-Profilern und interaktiven Profilern.
  • Post-Mortem Profiler.  Ein Post-Mortem-Profiler sammelt fortlaufend Daten über den Programmablauf und schreibt alle Erkenntnisse in eine Datei, wenn die Applikation endet.  Die Analyse der Daten erfolgt naturgemäß nach dem Programmablauf, deshalb „post mortem“.  Für Server-Programme, die meist länger laufen, muss man im Fall eines Post-Mortem-Profilers das Programm explizit beenden, um überhaupt an das Messergebnis zu kommen.
  • Interaktives Profiling. Ein interaktiver Profiler erhebt ebenfalls fortlaufend Daten und macht diese sofort verfügbar.  Dabei werden die Analysedaten nicht wie beim Post-Mortem-Profiler in eine Datei geschrieben, sondern die Anzeige der Daten erfolgt typischerweise in einem GUI.  Interaktive Profiler haben den Vorteil, dass man nicht bis zur Beendigung der Anwendung warten muss, ehe die Profiling-Daten analysiert werden können.
Man sieht also, dass nicht jeder Profiler für jede Anwendung gleich gut geeignet ist.

Kommerzielle Profiler sind in der Regel interaktiv, während nicht-kommerzielle Tools die Daten oft nur post-mortem bereitstellen; bisweilen sind beide Ansätze kombiniert. Die Unterscheidung zwischen Post-Mortem- und interaktiver Datenbereitstellung ist also, neben der unterschiedlichen Bedienung, zu einem gewissen Grad ein Kriterium für die Güte des Profiler-Tools.  Das liegt daran, dass ein interaktiver Profiler schwerer zu bauen ist, weil er die Daten nicht nur laufend abliefert, sondern sie auch gleich für die Analyse aufbereitet.
 

Wie erheben Profiler ihre Daten?

Sehen wir uns als nächstes an, wie die Profiler ihre Daten erheben.  Dabei sind zwei Aspekte erwähnenswert.
  • Instrumentierung. Das Tool verändert für den Zweck des Profilings den Binärcode des Programms, indem Anweisungen für die Datenerhebung, die Zeitmessung und ähnliches eingefügt werden.  Das bezeichnet man als Instrumentierung.  In Java passiert diese Instrumentierung in der Regel, wenn eine Klasse vom Class Loader geladen wird.  Die Instrumentierung ist ein Eingriff in die Anwendung und kann u.U. zu spürbaren Verfälschungen der Messergebnisse führen.  So kann eine Instrumentierung beispielsweise dazu führen, dass die zu untersuchende instrumentierte Anwendung erheblich langsamer läuft als die normale Anwendung ohne Instrumentierung.
  • Schnittstellen der Ablaufumgebung. Profiler-Tools stützen sich auf Schnittstellen der Ablaufumgebung ab, um beispielsweise Zeitstempel zu nehmen oder Daten über die laufende Anwendung abzugreifen. Das können Schnittstellen des Betriebssystems sein oder in Java auch Schnittstellen der Virtuellen Maschine.   Die Virtuelle Maschine stellt dafür ein spezielles Tool-Interface zur Verfügung, das als JVMPI/JVMTI bezeichnet wird.
Da der Artikel dem Profiling von Java-Anwendungen gewidmet ist, sehen wir uns im Folgenden die JVMPI/JVMTI-Schnittstellen genauer an.  Genaue Kenntnis der Schnittstellen ist nicht erforderlich für die Bedienung von Profiler-Tools, die diese Schnittstellen verwenden.  Aber prinzipielle Kenntnisse über JVMPI/JVMTI erleichtern das Verständnis der Arbeitsweise dieser Profiler-Tools und erhöhen damit letztlich die Verlässlichkeit der Messdatenanalyse.
 

Das Java Profiling und Tool Interface JVMPI/JVMTI

Die virtuelle Maschine bietet zur Unterstützung von Tools und Debuggern eine Reihe von Schnittstellen an, die in der sogenannten Java Platform Debugger Architecture (JPDA) beschrieben sind.  Teil dieser Unterstützung ist seit dem JDK 1.2 das Java Virtual Machine Profiler Interface, kurz JVMPI genannt.  Diese Schnittstelle ist die Basis für die Implementierung von Profiling-Werkzeugen, wie wir sie in diesem Artikel betrachten wollen.  Seit der Version 5.0 von Java gibt es als Alternative das Java Virtual Maschine Tool Interface, kurz JVMTI genannt.  Das JVMTI soll das alte JVMPI in der Zukunft vollständig ablösen, möglicherweise schon in der Version 6.0 von Java.

Beide Schnittstellen bieten vergleichbare Funktionalität.  Auf die Unterschiede zwischen den beiden Schnittstellen wollen wir in diesem Beitrag nicht eingehen.  (Für weitergehende Information siehe / JVMPI /, / JVMTI / und / TRANS /.) Für den Java-Entwickler ist nämlich relativ uninteressant, auf welche dieser beiden Schnittstellen ein verwendetes Profiler-Tool aufsetzt.  Im Moment verwenden die meisten Tools ohnehin noch die alte JVMPI-Schnittstelle. Der einzig spürbare Unterschied für den Werkzeug-Benutzer sollte die Stabilität der Tools sein.  Die alte JVMPI-Schnittstelle hatte diverse, allgemein bekannte Probleme, die gelegentlich dazu geführt haben, dass keine oder nur unvollständige Messdaten erhoben werden konnten.  Diese Probleme sollen nun mit der neuen JVMTI-Schnittstelle behoben sein.
 

Architektur von JVMPI/JVMTI-basierten Werkzeugen

Sehen wir uns die grundsätzliche Architektur von Profiler-Werkzeugen an, die mit der JVMPI- oder JVMTI-Schnittstelle arbeiten (siehe Abbildung 1).


Abbildung 1: Profiler-Tool-Architektur

Die virtuelle Maschine, in der die zu messende Anwendung läuft, liefert über die JVMPI- bzw. JVMTI-Schnittstelle Informationen an einen Agenten.  Der Agent gibt diese Informationen an ein Profiler-Frontend, das die Informationen speichert, analysiert und aufbereitet.

Wie die Kommunikation zwischen Agent und Frontend erfolgt, ist vollständig dem Profiler-Tool selbst überlassen.  Das Frontend kann zusammen mit dem Agenten im selben Prozess ablaufen; dann geht die Kommunikation einfach über den Speicher. Das ist allerdings eher selten.  In der Regel läuft das Frontend in einem eigenen Prozess auf derselben Maschine oder auch remote auf einem anderen Rechner.  Die Kommunikation zwischen Agent und Frontend kann dann über ein beliebiges Netz-Protokoll erfolgen. JVMPI/JVMTI macht diesbezüglich keine Vorgaben; meistens wird über TCP/IP kommuniziert.

Die Profiler-Agenten sind typischerweise in C oder C++ implementiert, weil die JVMPI/JVMTI-Schnittstellen in C spezifiziert sind.  JVMPI/JMVTI hat damit gewisse Ähnlichkeiten mit JNI, wo ebenfalls Information über C/C++-Schnittstellen ausgetauscht wird.

Für die Bereitstellung des Profiler-Agenten gibt es zwei Ansätze:  separate Bibliothek oder Integration ins Profiler-Tool.

  • Separate Bibliothek. Einige Profiler-Tools liefern den Agenten als Native Library aus, die dann von der JVM angesprochen wird.  Den Namen dieser Native Library gibt man beim Starten der Virtuellen Maschine über eine entsprechende JVM-Option an; die Option heißt –Xrun<jvmpi-agent-library-name> (bzw. –agentlib:<jvmpi-agent-library-name> in Java 5.0). Die Virtuelle Maschine lädt und startet den Agenten dann von sich aus.  Anschließend muss das gestartete Profiler-Frontend  (ähnlich wie beim Remote Debugging) mit dem Profiler-Agenten verbunden werden. Die Bereitstellung als separate Bibliothek ist zwar etwas umständlicher bei der Installation und Bedienung des Tools, hat aber den Vorteil, dass die Anwendung beim Profiling ganz normal in der JVM abläuft, wie auch sonst ohne Profiling.
  • Integration ins Tool. Andere Profiler-Tools integrieren den Agenten in die virtuelle Maschine.  Das Starten von JVM, Profiler-Agent, Profiler-Frontend und Anwendung ist in das Profiler-Tool integriert und der Benutzer sieht die einzelnen Schritte nicht mehr. Das ist in der Bedienung einfacher, weil es nicht passieren kann, dass die JVM den Agenten nicht findet oder der Agent das Frontend kann nicht findet. Die Lösung hat aber den Nachteil, dass die zu messende Anwendung nicht wie sonst in der normalen JVM abläuft, sondern in der Spezial-JVM des Tools gestartet werden muss.
Manche Tools unterstützen beide Ansätze.  Dann steht man vor der Entscheidung, wo man die Parameter der Anwendung und des Profiler am einfachsten angibt: entweder beim separaten Start der Anwendung (das ist günstig, wenn es zahlreiche Anwendungsparameter gibt und sowieso schon ein Batchfile/ShellScript für den Start der Anwendung existiert) oder aus dem Tool heraus (das ist günstig, wenn der Profiler-Agent komplizierte Parameter hat, die dann das Tool anhand von Benutzereingaben am GUI selbst zusammenstellt).
 

Funktionalität von JVMPI/JVMTI

Die JVMPI/JVMTI-Schnittstellen bieten dem Profiler Funktionalität für die Erhebung unterschiedlicher Informationen über das ablaufende Java-Programm. Dazu gehören:
  • Runtime-Information.  Hier findet man Information über alle augenblicklich geladenen Klassen, alle Threads in der JVM, den aktuellen Stack-Trace, die aktuellen Werte von Feldern, und ähnliches.
  • Metadaten.  Das sind Informationen über die Klassen, d.h. über deren Felder und Methoden, etwa der Information entsprechend, die man auch über Reflection beschaffen kann.
  • Weitere Informationen.  Zum Beispiel die Zeit (für Zeitmessungen), die verbrauchte CPU-Zeit pro Thread, ...
  • Callbacks.  Wenn bestimmte Ereignisse eintreten, dann werden von der JVM Callback-Methoden des Profiler-Agenten aufgerufen.  Zu den interessanten Ereignissen gehören:
    • Eintritt in eine Methode und Austritt aus einer Methode.
    • ein Pop auf den Stackframe, das Fangen einer Exception, der Zugriff auf ein Feld.
    • Anfang und Ende eines Threads, Anfordern und Freigeben eines Monitors (d.h. Eintritt/Austritt eines synchronized Blocks).
    • Speicherallokation für ein Objekt.
    • ...
Der Informationsfluss kann dabei von beiden Beteiligten ausgelöst werden.  Die Information kann aktiv vom Agenten bei der JVM angefordert werden oder aber sie wird über den Event-Callback-Mechanismus von der JVM an den Agenten geliefert.

Sehen wir uns an einem Beispiel an, wie das funktioniert.  Nehmen wir an, der Profiler will Informationen darüber beschaffen, wie oft jede einzelne Methode der Anwendung aufgerufen wird und wie viel Zeit in jeder Methode verbracht wird.  Die entsprechende Anfrage wird der Benutzer im Profiler-Frontend stellen.  Das Frontend reicht die Anfrage an den Agenten weiter.  Der Agent meldet über JVMPI/JVMTI entsprechende Callback-Methoden bei der JVM an, die beim Eintreten und Verlassen von Methoden später von der JVM aufgerufen werden.  Die Callback-Methoden zählen die Anzahl der Aufrufe je Methode und messen die CPU- und Elapsed-Zeit, die in der Methode verbracht wird.  Die notwendigen Zeitstempel kann der Agent von der JVM anfordern.  Der Agent schickt die ermittelte Daten an das Frontend, welches die Information in irgendeiner Form speichert. Nach dem Testlauf wird die Information analysiert, aufbereitet und dem Anwender angezeigt.

Wie die Darstellung der ermittelten Profiling-Information aussieht, hängt ganz vom Frontend ab.  Typische Darstellungsvarianten sind:

  • Liste aller gerufenen Methoden (z.B. sortiert nach der Aufrufhäufigkeit oder dem Zeitverbrauch) oder alternativ graphische Darstellung aller gerufenen Methoden als Aufruf-Graph.
  • Angabe der akkumulierten Zeit, die in einer Methode und all ihren Sub-Methoden verbracht wurde, oder alternativ des Zeitverbrauchs der Methode allein, d.h. ohne die Sub-Methoden.
  • Anzeige der CPU-Zeit oder alternativ der Elapsed-Zeit.
  • Angabe der akkumulierten Zeit, die in einer Methode verbracht wurde, oder alternativ des durchschnittlichen Zeitverbrauchs pro Methodenaufruf.
Nach diesem Einblick in die Prinzipien der Architektur von Java-Profiler-Werkzeugen wollen wir uns ansehen, welche Profiler-Tools es  derzeit gibt.
 

Profiler-Tools – ein kurzer Überblick über die Tool-Landschaft

Im Bereich der Profiler-Tools folgt  Sun Microsystems seiner generellen Philosophie, gemäß derer Sun die Spezifikation einer Java-Technologie im Rahmen des Community Process erarbeitet und die Implementierung anderen Herstellern überlässt.  Das ist ganz genauso wie bei EJB oder Servlets. Sun liefert als Proof-of-Concept eine Implementierung mit rudimentärer Funktionalität, und überlässt die Entwicklung von Lösungen für den professionellen Einsatz den Tool-Herstellern und der Open Source Community.  Üblicherweise findet man am Ende 3 Level vor:
  1. Rudimentäre Lösungen, wie zum Beispiel den Proof-of-Concept von Sun selbst, oder aber auch solche, die von Enthusiasten und Bastlern gebaut worden sind.
  2. Open Source Implementierungen.
  3. Kommerzielle und damit kostenpflichtige Produkte.
Im Falle der Profiler gibt es keine ernstzunehmende Open Source Initiative zur Herstellung eines Profilers, anders als das beispielsweise im J2EE-Bereich mit Tomcat und JBoss der Fall ist.  Es bleiben also nur die rudimentären und die kommerziellen Tools zur Auswahl.

Den Proof-of-Concept hat Sun mit dem sogenannten HPROF geliefert (siehe / HPROF /).  Der HPROF ist ein JVMPI/JVMTI-Agent mit einem sehr einfachen Frontend, das lediglich Binär- oder Textausgabe erzeugt und nicht interaktiv ist. Da die Ausgabe nicht unbedingt einfach zu analysieren ist, gibt es eine Reihe von Auswertungstools, die den HPROF-Output aufbereiten und in lesbarer und verständlicher Form anzeigen.  Zu diesen kostenlosen Tools gehört unter anderem der HPjmeter von Hewlett-Packard (siehe / HPJM /), aber auch das Heap-Analysis-Tool HAT von Sun selbst (siehe / HAT /).

Die kommerziellen Tools unterscheiden sich von den kostenlosen durch die Güte der Darstellung der Profiling-Daten und durch die Unterstützung bei der Auswertung der Daten.  Zusätzlich bieten die kommerziellen Tools in der Regel Wizards, die dem Benutzer helfen, aus den Daten die Schwachstellen des Programms herauszulesen. Beispielsweise ist die Identifikation von Memory-Leaks nicht unbedingt trivial.  Bei den rudimentären Tools muss man sich die Profiling-Information mühselig zusammensuchen und sorgfältig analysieren, während ein Wizard in einem kommerziellen Tool diesen Vorgang optimal unterstützt, die Analyse selbständig vornimmt und Hinweise auf mögliche Memory Leaks gibt.  Die wohl populärsten Tools in dieser Liga sind JProbe von Quest Software (siehe / JPROBE /), JProfiler von ej-technologies GmbH (siehe / JPROF /), und YourKit von YourKit, LLC(siehe / YOURK /).

Es gibt aber eine Menge Alternativen.  Eine gute Übersicht, sowohl über die freien als auch die kommerziellen Tools, findet man im Internet (siehe / TUNE /).

Das eigentliche Profiling

Als Ausblick auf die nachfolgenden Beiträge der Kolumne wollen wir uns noch kurz ansehen, was man mit den Profiler-Tools überhaupt anfangen kann. Mit Hilfe der JVMPI/JVMTI-Schnittstellen können Profiler-Tools folgende  Tätigkeiten während der Entwicklung unterstützen:
  • Functional-HotSpots. Performance-Profiling wird hauptsächlich gemacht, um funktionale Performance-Engpässe zu identifizieren, also Methoden, die sehr viel Zeit verbrauchen, weil sie entweder sehr oft aufgerufen werden, oder weil sie sehr lange brauchen für jeden einzelnen Ablauf.  Solche Methoden werden anschließend im Rahmen eines Performance-Tunings entsprechend überarbeitet und in Hinsicht auf ihre Performance optimiert.  Danach überprüft man die Optimierung im Rahmen eines erneuten Profilings.
  • Memory-Allocation-HotSpots.   Hierbei versucht man sich einen Überblick über den Speicherverbrauch der Anwendung zu verschaffen, mit dem Ziel, den Memory Footprint des Programms insgesamt zu minimieren.  Dabei sucht man nach Object-Creation-HotSpots, also Stellen im Programm, wo auffallend hoher Speicherverbrauch festzustellen ist. Möglicherweise werden dort Objekte unnötig erzeugt und man kann diese Speicherverschwendung beseitigen.  Oder die Objekte werden gebraucht; dann kann man immer noch überlegen, ob sich der Speicherverbrauch durch Wiederverwendung von bereits allozierten Objekten reduzieren lässt.
  • Memory-Leaks. Zum Memory-Profiling gehört auch die Suche nach Memory Leaks, also nach Speicher, der logisch gar nicht mehr gebraucht wird, aber immer noch über eine unerwünschte Referenz erreichbar ist und damit vom Garbage Collector nicht aufgeräumt werden kann.  Solche Programmierfehler kann man dann korrigieren, wenn man sie mittels eines Memory.-Profilings gefunden hat.
  • Thread-Contention. Dabei geht es um die Verfolgung der Thread-Zustände und insbesondere um die Identifikation und Vermeidung von Starvation und Deadlocks.  Es wird verfolgt, welche Threads welche Monitore halten und freigeben, und kann daran erkennen, ob sich Threads unnötig behindern oder blockieren.  Auf diese Weise können Fehler in der Synchronisierung, wie zum Beispiel Deadlocks, festgestellt werden.
  • Thread-Aktivität. Mit Hilfe eines Profilers kann man prüfen, ob die Threads der Anwendung die CPU so nutzen, wie man es erwartet.  (Beispiel: bei einem Thread, der hauptsächlich rechnet und verarbeitet, würde man eine hohe CPU-Nutzung erwarten; bei einem Thread, der an einem Socket hängt und auf Requests wartet, wird man eher eine geringe CPU-Nutzung erwarten.) Wenn der CPU-Verbrauch der Threads nicht den Erwartungen entspricht, dann hat man entweder die falschen Erwartungen die Thread-Aktivitäten betreffend oder die Implementierung ist tatsächlich fehlerhaft und muss korrigiert werden.  Das Ergebnis einer Korrektur würde dann in einem erneuten Profiling überprüft.
  • Code Coverage.  Das ist eher ein Nebenprodukt des Profilings. Da die JVMPI/JVMTI-Schnittstelle die nötige Funktionalität ohnehin bereitstellt, können die Tools die Testabdeckung ohne nennenswerten Aufwand zur Verfügung stellen.  Es wird der Testüberdeckungsgrad gemessen und man kann daran die Güte und Vollständigkeit einer Testsuite messen.  Dabei stellt man u.U. fest, dass das Programm toten Code enthält, den man vielleicht besser eliminieren sollte.  Oder aber man stellt fest, daß die Tests  nicht ausreichend sind und vervollständigt seine Testsuite.
In den nächsten Beiträgen wollen wir uns die oben genannten Tätigkeiten genauer ansehen und entsprechende Strategien besprechen und demonstrieren.

Zusammenfassung

In diesem Artikel haben wir erläutert, was Profiling im Gegensatz zu Benchmarking oder Monitoring ist: es geht beim Profiling um die Erhebung von Daten über eine Anwendung mit dem Ziel, Schwachstellen im Programm zu identifizieren und anschließend zu beseitigen. Da ein Profiling ohne Unterstützung durch Tools kaum möglich ist, haben wir uns angesehen, wie Profiler-Tool ganz allgemein arbeiten: welche Daten sie erheben (Time-, Space- und Thread-Profiler), wann sie die Daten zu Analysezwecken bereitstellen (post-mortem oder interaktiv) und wie die Daten gewonnen werden (per Schnittstellen der Ablaufumgebung oder Instrumentierung des Programms).  Für das Profiling in Java ist die Java Tool Schnittstelle JVMPI/JVMTI von zentraler Bedeutung.  Sie erfordert, dass das Tool einen Agenten hat, der mit der virtuellen Maschine kommuniziert und die Messdaten anfordert und entgegennimmt.  Der Agent liefert die Daten an ein Frontend, das die Messdaten für die Analyse bereitstellt und ggf. aufbereitet.
 

Literaturverweise und weitere Informationsquellen

/PERF/ Java Performance 
URL: http://java.sun.com/docs/performance/
/JVMPI/  JavaTM Virtual Machine Profiler Interface (JVMPI)
URL: http://java.sun.com/j2se/1.4.2/docs/guide/jvmpi/jvmpi.html
/JVMTI/  Creating a Debugging and Profiling Agent with JVMTI
URL: http://java.sun.com/developer/technicalArticles/Programming/jvmti/
/TRANS/  The JVMPI Transition to JVMTI
URL: http://java.sun.com/developer/technicalArticles/Programming/jvmpitransition/#1
/TUNE/  Java Performance Tuning
Ein Überblick über vermutlich die meisten verfügbaren Profiler Tools zum Teil mit Beschreibung und Bewertung.
URL: http://www.javaperformancetuning.com/resources.shtml
URL: http://www.javaperformancetuning.com/tools/index.shtml
/HPROF/  HPROF: A Heap/CPU Profiling Tool in J2SE 5.0
Ein Artikel über die neue HPROF-Architektur in Java 5.0 vom November 2004.
URL: http://java.sun.com/developer/technicalArticles/Programming/HPROF.html 

Java Performance Tuning and Memory Management
Ein Tutorial über Performance-Tuning aus dem Jahr 2002, in dem Details zu HPROF beschrieben sind.
URL: http://tutorials.beginners.co.uk/read/category/82/id/213/p/1

Tool Report: Hpjmeter
Ein Bewertung aus dem August 2002. 
URL: http://www.javaperformancetuning.com/tools/hpjmeter/index.shtml

/HPJM/ HPjmeter 1.6
Die Toolseite von Hewlett-Packard mit Download und Informationen.
URL: http://www.hp.com/products1/unix/java/hpjmeter/index.html 

Tool Report: HPjmeter
Eine Beschreibung und Bewertung des HPjmeter, die u.a. die wesentlichen Feature beschreibt vom August 2002.
URL: http://www.javaperformancetuning.com/tools/hpjmeter/index.shtml

/HAT/ Heap Analysis Tool 1.1 (HAT)
Die Toolseite von Hewlett-Packard mit Download und Informationen.
URL: https://hat.dev.java.net/
/JPROBE/ Tool Report: JProbe
Eine Bewertung vom April 2003. 
URL: http://www.javaperformancetuning.com/tools/jprobe/index.shtml

Tool-Hersteller: Quest Software
URL: http://www.quest.com/jprobe/

/JPROF/ Tool Report: JProfiler
Eine Bewertung vom Juni 2002.
URL: http://www.javaperformancetuning.com/tools/jprofiler/index.shtml

Tool-Hersteller: ej-technologies
URL: http://www.ej-technologies.com/products/jprofiler/overview.html

/YOURK/ Tool-Hersteller: YourKit
URL: http://www.yourkit.com/

Die gesamte Serie über Java Performance:

/KRE1/  Java Performance, Teil 1: Was ist ein Micro-Benchmark?
Klaus Kreft & Angelika Langer
Java Spektrum, Juli 2005
URL: http://www.AngelikaLanger.com/Articles/EffectiveJava/21.MicroBenchmarking/21.MicroBenchmarking.html
/KRE2/  Java Performance, Teil 2: Wie wirkt sich die HotSpot-Technologie aufs Micro-Benchmarking aus?
Klaus Kreft & Angelika Langer
Java Spektrum, September 2005
URL: http://www.AngelikaLanger.com/Articles/EffectiveJava/22.JITCompilation/22.JITCompilation.html
/KRE3/  Java Performance, Teil 3: Wie funktionieren Profiler-Tools?
Klaus Kreft & Angelika Langer
Java Spektrum, November 2005
URL:  http://www.AngelikaLanger.com/Articles/EffectiveJava/23.ProfilingTools/23.ProfilingTools.html
/KRE4/ Java Performance, Teil 4: Performance Hotspots - Wie findet man funktionale Performance Hotspots?
Klaus Kreft & Angelika Langer
Java Spektrum, Januar 2006
URL:  http://www.AngelikaLanger.com/Articles/EffectiveJava/24.FunctionalHotSpots/24.FunctionalHotSpots.html
/KRE5/ Java Performance, Teil 5: Performance Hotspots - Wie findet man Memory Hotspots?
Klaus Kreft & Angelika Langer
Java Spektrum, März 2006
URL:  http://www.AngelikaLanger.com/Articles/EffectiveJava/25.MemoryHotSpots/25.MemoryHotSpots.html
/KRE6/ Java Performance, Teil 6: Garbage Collection - Wie funktioniert Garbage Collection?
Klaus Kreft & Angelika Langer
Java Spektrum, Mai/Juli 2006
URL:  http://www.AngelikaLanger.com/Articles/EffectiveJava/26.GarbageCollection/26.GarbageCollection.html
/KRE7/  Java Performance, Teil 7: Garbage Collection - Das Tunen des Garbage Collectors
Klaus Kreft & Angelika Langer
Java Spektrum, September 2006
URL:  http://www.AngelikaLanger.com/Articles/EffectiveJava/27.GCTuning.html/27.GCTuning.html

 
 

If you are interested to hear more about this and related topics you might want to check out the following seminar:
Seminar
 
High-Performance Java - programming, monitoring, profiling, and tuning techniques
4 day seminar ( open enrollment and on-site)
 
  © Copyright 1995-2008 by Angelika Langer.  All Rights Reserved.    URL: < http://www.AngelikaLanger.com/Articles/EffectiveJava/23.ProfilingTools/23.ProfilingTools.html  last update: 26 Nov 2008