Zum Inhalt springen
  • Von: Bernd Müller
  • Java Development
  • 04.07.2012

Unbekannt aber nützlich im SDK: Speichernutzung und Garbage Collection mit VisualVM

VisualVM wird vom Hersteller als das „All-in-One Java Troubleshooting Tool“ im SDK bezeichnet. Mit ihr können Entwickler unter anderem den Speicherbedarf einer Anwendung sowie das Garbage-Collection-Verhalten einer VM analysieren. DOAG Online zeigt im Überblick, wie Visual VM funktioniert.

Das Java SDK enthält bereits seit den allerersten Versionen eine Reihe von Kommandozeilen-Werkzeugen für die Untersuchung von Anwendungen, wie etwa „jps“, „jstat“, „jstatd“, „jinfo“, „jhat“, „jmap“ und „jstack“. Mit dem SDK 5 wurde „jconsole“ eingeführt, ein Monitoring-Werkzeug, das Informationen über die Performanz und den Ressourcen-Verbrauch von laufenden Java-Anwendungen grafisch darstellt. VisualVM entstand im Rahmen des NetBeans-Projekts und wurde mit dem Update 7 des SDK 6 eingeführt. Als Stand-alone-Anwendung kann sie hier heruntergeladen werden; in neueren SDKs ist sie - wie bereits erwähnt - enthalten. 

VisualVM im Überblick

Nach dem Start von VisualVM (Programm „jvisualvm“ im „bin“-Verzeichnis des SDK) erscheint das Hauptfenster (Abbildung 1). Wir verwenden VisualVM in der Version des SDK 6 Update 26. Man erkennt im linken Teilfenster den Tab „Applications“ mit den vier Knoten „Local“, „Remote“, „VM Coredumps“ und „Snapshots“. VisualVM erlaubt das Monitoring und Profiling von lokalen und entfernten JVMs. Dafür müssen einige zusätzliche Vorkehrungen getroffen werden, auf die wir hier jedoch nicht eingehen werden. Weiterhin besteht die Möglichkeit, Coredumps zu analysieren sowie Snapshots zu erzeugen und ebenfalls zu analysieren. Auf der rechten Seite erkennt man im Tab „Start Page“ einige Überschriften, die Web-Links darstellen. Wir empfehlen dem Leser zum Einstieg in die SDK-Bordwerkzeuge folgende Links:

Zurück zu den lokalen Anwendungen: Man erkennt VisualVM selbst, GlassFish, JMeter und Eclipse. Durch einen Doppelklick kann die zu monitorende Anwendung ausgewählt werden. Im rechten Teilfenster wird dann ein neuer Tab mit Informationen zur ausgewählten Anwendung dargestellt. Der Name des Tabs entspricht dem im linken Teilfenster selektierten Knoten. Der Tab selbst ist wiederum in Unter-Tabs aufgeteilt. Abbildung 2 zeigt den initialen Tab „Overview“. Die Tabs „JVM arguments“ und „System properties“ sollten die erste Anlaufstelle sein, wenn sich eine Anwendung unerklärlich verhält. Eventuell ist lediglich ein Property falsch definiert.

Monitoring von Anwendungen

Bei Server-Anwendungen kann es zu Speicherplatz-Problemen kommen, die sich durch einen „OutOfMemory“-Error oder häufige Garbage-Collection-Pausen bemerkbar machen. Zur Analyse derartiger Probleme können hochwertige kommerzielle Produkte für eine erste (und eventuell ausreichende) Analyse aber auch VisualVM eingesetzt werden. In einem kleinen Beispiel wollen wir Objekte auf dem Heap anlegen und wieder freigeben, um den Einsatz des Garbage Collectors zu erzwingen und die Speicherplatz-Belegung sowie den Einsatz des Garbage Collectors mit VisualVM visualisieren zu können.

Zunächst benötigen wir ein Programm, um Speicherplatz (sinnlos) anzulegen und wieder freizugeben. Wir wählen JAX-RS als Implementierungsalternative, da eine Anwendung mit Swing oder ein Menü über „System.in/System.out“ aufwändiger wäre. Der folgende Code legt pro Aufruf von „waste()“ eine Liste von String-Arrays wachsender Länge an und erzeugt durch eine unglückliche Verwendung von „+“ zur String Concatenation zusätzlichen Müll. Da die Liste eine lokale Variable der Methode ist, kann die ganze Struktur nach dem Methodenaufruf „garbage-collected“ werden. Die Details der Implementierung sind hier nicht von Interesse (siehe Listing 1).

//CODE:java:@Path("/")
public class WasteService {
  @GET
  @Path("waste/{size}")
  @Produces(MediaType.TEXT_PLAIN)
  public String waste(@PathParam("size") int size) {
    List<String[]> waste = new ArrayList<String[]>();
    for (int i = 1; i < size; i++) {
      waste.add(consume(i));
    }
    return "wasted";
  }

  private String[] consume(int i) {
    String[] waste = new String[i];
    waste[0] = new String("1");
    int digit = 1;
    for (int j = 1; j < waste.length; j++) {
      waste[j] = waste[j-1] + (++digit) % 10;
    }
    return waste;
  }
}//CODE

Listing 1

Durch die Definition als REST-Service und entsprechende Konfiguration von Anwendungsnamen (waste) und REST-Anwendungspfad (rest) kann mit jedem Browser, mit „wget“ oder „curl“ und dem folgenden URL der Speicher in Anspruch genommen werden: localhost. Dabei ist die Größe der Speicherstruktur frei wählbar. Auch der explizite Aufruf des Garbage Collectors kann einfach über REST realisiert werden (siehe Listing 2)

//CODE:java:@Path("/")
public class WasteService {
  ...
  @GET
  @Path("gc")
  @Produces(MediaType.TEXT_PLAIN)
  public String gc() {
    System.gc();
    return "gc called";
  }
  ...
}//CODE

Listing 2

Der Garbage Collector kann nun ebenfalls sehr einfach aufgerufen werden: localhost. Für den REST-erfahrenen Leser: Wir sind uns darüber im Klaren, dass „GET“ in beiden Fällen nicht die korrekte und von REST intendierte Alternative ist. Sie ist aber am einfachsten zu verwenden.

Zurück zur VisualVM. Unter dem Tab „Monitor“ erscheint eine Anzeige mit den vier Bereichen „CPU“, „Heap/PermGen“, „Classes“ und „Threads“ (siehe Abbildung 3). Da die Darstellung der Klassen und Threads für unser Beispiel irrelevant ist, können diese beiden Teilbereiche entfernt werden. Mit den beiden verbliebenen Bereichen „CPU“ und „Heap“ starten wir nun unseren Versuch. Mit „Jmeter“, einem Werkzeug für Load-Tests und Performanz-Messungen der Apache Software Foundation, rufen wir mehrfach die URL „http://localhost:8080/waste/rest/waste/{size}“ auf. Der konkrete Wert von „size“ ist nicht relevant und muss für die jeweilige Umgebung eventuell angepasst werden, um die gewünschte Speichernutzung zu erzwingen. Abbildung 4 zeigt die Darstellung in VisualVM.

Man erkennt, dass der mehrmalige Aufruf der Methode entsprechende Speicher-Allokationen verursacht hat. Um insgesamt mit dem Speicher auskommen zu können, hat die JVM den Garbage Collector mehrfach aufgerufen (Kurve „GC activity“ im linken Diagramm). Die JVM hat außerdem den Heap-Bereich auf 500 MB erweitert, was dem (konfiguriert) maximal verfügbaren Heap entspricht.

Das Plug-in „Visual GC“

Das Thema „Speichernutzung und Garbage Collection mit VisualVM“ wäre ohne das Plug-in „Visual GC“ nicht annähernd vollständig. VisualVM erlaubt durch einen einfachen Plug-in-Mechanismus die Erweiterung um verschiedene Aspekte. Eine Reihe von Plug-ins können hier heruntergeladen und installiert werden. Über das Menü „Tools -> Plugins“ geschieht dies über wenige Klicks, sodass durchaus von einem SDK-Bordmittel gesprochen werden kann. Wir gehen außerdem davon aus, dass in einer zukünftigen Version von VisualVM dieses Plug-in bereits enthalten sein wird. Abbildung 5 zeigt VisualVM nach der Installation von Visual GC und dem geöffneten Tab „Visual GC“.

Die Standard-Darstellung des Heap in VisualVM unterscheidet nicht zwischen den verschiedenen Heap-Bereichen (siehe Abbildung 4). Mit Visual GC ist es möglich, diese Bereiche einzeln zu beobachten. In Abbildung 5 ist der Bereich für die Old Generation und die Young Generation zu erkennen. Die Young Generation ist wiederum in die Bereiche „Eden“, „Survivor 0“ und „Survivor 1“ unterteilt. Oracles VM erlaubt unter anderem mit den Startoptionen „-Xmn“, „-XX:NewSize“, „-XX:MaxNewSize“, „-XX:NewRatio“ und „-XX:SurvivorRatio“ das Fein-Tuning des Heap. Um diese Optionen sinnvoll einsetzen zu können, muss zunächst das Verhalten dieser Speicherbereiche auf potenzielle Probleme hin analysiert werden. Die Verwendung von VisualVM ist eine Möglichkeit, dies zu tun.

Fazit

Mit VisualVM ist es möglich, den Speicherbedarf einer Anwendung und das Garbage-Collection-Verhalten einer VM zu analysieren. Das Profiling wurde hier außer Acht gelassen, bietet aber attraktive Möglichkeiten: Dabei kann etwa für eine Klasse die Anzahl der Instanzen und deren Speicherbedarf sowie die Laufzeit einer Methode ermittelt werden. Durch weitere Plug-ins, etwa für das Monitoring des GlassFish-Application-Servers oder die Anzeige von MBeans-Details, kann VisualVM mit zusätzlicher Funktionalität versehen werden. Empfohlen wird, VisualVM selber auszuprobieren und sich ein eigenes Bild zu verschaffen. Wer sich zu den genannten Themen informieren möchte, kann mehr zum Thema in dem Buch  "Java Performance" von Hunt und John erfahren.