Java Profiling: Under the Covers
At some point, most developers will get tasked with troubleshooting a performance issue. If at any time you've actually been lucky enough to get paid to worry about performance in general, you probably have a better toolkit (on your desktop and in your head) than the average developer. If, on the other hand, such an assignment is a relatively rare occurrence, you probably will rely on a combination of loggers, debuggers, various open-source profilers (how many companies have you worked at that will spring for a paid license?), and your old friend System.out.println().
This article is geared toward you if you are interested in the instrumentation hooks provided by the Java programming language. I won't be discussing available profilers, either open-source or otherwise; there is a lot of information on the Web about that topic. Here, you'll see how easily and quickly you can write your own profiler. Profilers tend to be rather expensive to run, although most have features (like selective instrumentation, sampling, etc.) that are designed to minimize their run-time impact. But they provide a general functionality, and if you learn how they are built, you can develop a compact profiler that is narrowly targeted to exactly what you need to do.
Overview
The first place you should look is the API docs for java.lang.instrument. This package is quite small, with only a handful of classes, but it is the entry point for our grassroots Java profiler. This package provides you with two key pieces of functionality:
A mechanism to intercept Java classes (specifically, their bytecode)
The ability to modify bytecode as it is being loaded
Conceptually, it's that simple. As examples, you could modify some or all classes to make calls to a profiling class on method entry and exit, or you could instrument every block of exception-handling code to generate an event for an event-processing agent.
Another topic touched on in the package instrumentation is the agent launching mechanism. You can launch your profiler either when your target application launches, or afterwards, via an attachment mechanism. As the package documentation notes, starting an agent at JVM startup is fully specified, while starting an agent after the JVM has started is implementation dependent. Java HotSpot implementations support agent start after JVM startup; I'll cover both approaches in this article.
A Minimal Instrumentation Agent
In this section, we'll build a minimal agent. It won't actually instrument any code, but it will provide the structure for doing so. The point is to get the agent launched and to at least note the bytecodes as they are loading.
Two things you must need to know to build this agent are
How will you launch it?
What is the mechanism for intercepting the classes as they are loaded?
The first question is answered in the java.lang.instrument package documentation. Basically, you:
Provide one of the two method signatures in your profiler:
public static void premain(String agentArgs, Instrumentation inst)
public static void premain(String agentArgs)
Add a line to the manifest file of your profiler's .jar file:
Premain-Class: fully qualified name of your profiler class
Launch your target (to-be-profiled) application with the following command-line option:
-javaagent:jarpath[=options]
Now, let's build an example. Create a new class, com.example.profiler.Profiler, as follows:
package com.example.profiler;
import java.lang.instrument.Instrumentation;
public class Profiler
{
public static void premain(String agentArgs, Instrumentation inst)
{
System.out.println("premain");
}
}
Next, create a manifest.mf file with the following single line:
Premain-Class: com.example.profiler.Profiler
Compile the class and build a .jar file, including the above manifest. You now have an agent. Choose a Java application and insert the following option into its launcher:
-javaagent: path to jar file
and launch the target application from a shell or command prompt. You should see the single line
premain
before any output from your target application. Obviously, this "profiler" does not instrument your target application, but it represents the bare minimum implementation required for a valid instrumentation agent.
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)






Comments
Eric Meallier replied on Tue, 2009/12/08 - 6:13am
hard to get this working !
during the instrumentation, a RuntimeException was throwed by getMethods() call. The problem comes from the naming convention of the classes.
The classname given by transform is like this test/Test. The classpool understand this syntax but store test.Test. When you are looking for the class (getClass() just before getMethods) the class is lazy loaded (the byte code is not introscepted) but is not good. the problem only appear when the getMethods is called.
A workaround is to call classPool.get(className.replace('/', '.'));
Thanks for the article.
Victor Tsoukanov replied on Fri, 2010/02/19 - 1:14am