29.01.2013 Aufrufe

Java Performance Tools – Teil 1 (Grundlagen)

Java Performance Tools – Teil 1 (Grundlagen)

Java Performance Tools – Teil 1 (Grundlagen)

MEHR ANZEIGEN
WENIGER ANZEIGEN

Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.

YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.

Sonderdruck<br />

hinzugefügt oder ein bestehender Code<br />

verändert. Der Zeitpunkt der Manipulation<br />

ist allerdings je nach verwendeter<br />

Technologie unterschiedlich. Bei der statischen<br />

Bytecode-Instrumentierung wird<br />

der Bytecode vor dem Laden der Klassen<br />

mit einem Preinstrumentor instrumentiert.<br />

Der Bytecode läuft daher auf jeder<br />

JVM und es werden keine zusätzlichen<br />

Anpassungen oder Libraries zur Laufzeit<br />

benötigt. Nachteilig ist, dass ein manueller<br />

Schritt für die Instrumentierung notwendig<br />

ist und Systemklassen des Runtime<br />

Environment nicht instrumentiert<br />

werden können.<br />

Bei der Load-Time-Bytecode-Instrumentierung<br />

wird ein eigener Class-<br />

Loader verwendet, der beim Laden<br />

einer Klasse den entsprechenden Bytecode<br />

einfügt. Alternativ kann auch ein<br />

JVMPI-oder JVMTI-Agent verwendet<br />

werden, um beim entsprechenden Classloading<br />

Event den Inhalt der Klasse zu<br />

verändern. Man spart also im Gegensatz<br />

zum statischen Ansatz einen manuellen<br />

Schritt und kann auch Systemklassen instrumentieren.<br />

Dynamische Bytecode-Instrumentierung<br />

erlaubt es, den Bytecode einer Klasse<br />

auch nachdem sie bereits geladen ist, zu<br />

verändern. Diese Möglichkeit existiert<br />

erst seit <strong>Java</strong> 5 und wird daher erst von<br />

sehr wenigen <strong>Tools</strong> unterstützt. Das dynamische<br />

Instrumentieren von Klassen<br />

wird durch die RedefineClasses-Funktion<br />

in JVMTI möglich. Neben JVMTI bietet<br />

<strong>Java</strong> 5 aber auch eine reine <strong>Java</strong>-Implementierung<br />

von Agenten für die dynamische<br />

Instrumentierung von Bytecode,<br />

die sich im Paket java.lang.instrument<br />

befindet. Eigene Agenten zu schreiben, ist<br />

relativ einfach, aber nicht nur für <strong>Performance</strong>-Messungen<br />

sinnvoll, deshalb soll<br />

nachfolgend beispielhaft die Implementierung<br />

eines Agenten in <strong>Java</strong> beschrieben<br />

werden.<br />

Das Package java.lang.instrument<br />

bietet Agenten die Möglichkeit, den<br />

Bytecode von Klassen dynamisch zu<br />

verändern. Die Manipulation des Bytecodes<br />

kann auf zwei Wegen geschehen.<br />

Zum einen zur Ladezeit durch eine Implementierung<br />

des Interfaces ClassFile-<br />

Transformer. Zum anderen zur Laufzeit,<br />

das heißt, auch nachdem Klassen bereits<br />

durch einen ClassLoader geladen wurden.<br />

Letzteres geschieht durch die Methode<br />

redefineClasses(), welche im Interface<br />

Instrumentation definiert ist. Zur Veranschaulichung<br />

soll ein einfacher Agent<br />

entwickelt werden, der beim Laden jeder<br />

Klasse eine Meldung mit dem Namen der<br />

Klasse auf der Konsole ausgeben soll.<br />

Die Dokumentation des java.lang.instrument-Pakets<br />

gibt eine Übersicht über<br />

Anforderungen an einen Agenten und<br />

wie dieser eingebunden wird. Um einen<br />

eigenen Agenten zu entwickeln, wird eine<br />

Bootstrap-Klasse benötigt. Diese muss<br />

über eine Methode mit einer Signatur verfügen,<br />

die vergleichbar mit der main-Methode<br />

für <strong>Java</strong>-Applikationen ist:<br />

public static void premain(String agentArgs,<br />

...<br />

}<br />

Instrumentation instrumentation){<br />

Der Methode werden die Parameter für<br />

den Agenten übergeben sowie eine Instanz<br />

vom Typ java.lang.Instrumentation,<br />

die Funktionen zur Instrumentierung von<br />

<strong>Java</strong>-Klassen bietet. Die Veränderung des<br />

Bytecodes erfolgt entweder über eine eigeneClassFileTransformer-Implementierung<br />

für die Manipulation beim Laden<br />

oder Redefinition der Klasse oder direkt<br />

über eine neue Klassendefinition vom<br />

Typ ClassDefinition, wenn eine Klasse<br />

geändert werden soll, die bereits geladen<br />

ist. Wird der Bytecode einer bereits geladenen<br />

Klasse mit der redefineClasses()-<br />

Methode überschrieben, hat dies nur<br />

Auswirkungen auf zukünftig aufgerufene<br />

Instanzen der Klasse. Instanzen der Klasse<br />

die sich in einem aktiven Stack Frame<br />

befinden, bleiben von der Änderung unberührt.<br />

In dem Beispiel wird ein eigener<br />

ClassFileTransformer verwendet, um<br />

die Namen der geladenen Klassen auf die<br />

Konsole zu schreiben und sie dann unverändert<br />

weiterzureichen. Die Implementierung<br />

des Interface benötigt nur eine<br />

Methode:<br />

package de.codecentric.transformer;<br />

import java.lang.instrument.ClassFileTransformer;<br />

import java.lang.instrument.IllegalClassFormatException;<br />

import java.security.ProtectionDomain;<br />

public class LogClassFileTransformer implements<br />

ClassFileTransformer {<br />

public byte[] transform(ClassLoader loader,<br />

String className,<br />

Class classBeingRedefined,<br />

ProtectionDomain protectionDomain,<br />

byte[] classfileBuffer)<br />

throws IllegalClassFormatException {<br />

System.out.println(<br />

“Transformiere die Klasse ‘“ + className + “’“);<br />

return null;<br />

}<br />

}<br />

Nachdem der LogClassFileTransformer<br />

mit addTransformer() bei der Instrumentation-Instanz<br />

registriert wurde, wird bei<br />

jeder Definition oder Redefiniton einer<br />

Klasse die Methode transform(…) aufgerufen.<br />

Eine Klassendefiniton erfolgt<br />

über ClassLoader.definineClass() während<br />

Klassenredefinitionen über Instrumentation.redefineClasses()<br />

erfolgen.<br />

Innerhalb der Methode ist es möglich,<br />

den Bytecode der Klasse zu verändern.<br />

Der Bytecode muss dem Classfile-Format<br />

der JVM-Spezifikation [2] entsprechen.<br />

An dieser Stelle bieten sich zur vereinfachten<br />

Manipulation verschiedene<br />

Open-Source-<strong>Tools</strong> wie Apache BCEL,<br />

ObjectWeb ASM [3] oder JBoss <strong>Java</strong>ssist<br />

[4] an. In unserem Beispiel wird die<br />

Klasse nicht verändert, weshalb in der<br />

transform()-Methode null zurückgeliefert<br />

wird. Der Code für den Agenten<br />

sieht wie folgt aus:<br />

package de.codecentric.instrument;<br />

import java.lang.instrument.Instrumentation;<br />

import de.codecentric.transformer.<br />

LogClassFileTransfor-<br />

Sonderdruck www.javamagazin.de<br />

mer;<br />

public class JMVTIAgent {<br />

public static void premain(String agentArguments,<br />

Instrumentation instrumentation) {<br />

}<br />

}<br />

instrumentation.addTransformer(<br />

new LogClassFileTransformer());<br />

Der Agent kann jetzt mit jeder beliebigen<br />

<strong>Java</strong>-Anwendung zusammen gestartet

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!