I'm a software architect/consultant in Boulder, Colorado. I started blogging at: http://wayne-adams.blogspot.com/, but have since started the new blog, Data Sceintist in Training which will cover the areas I am learning as part of my own "big data" training, including the Hadoop family, frameworks like OpenStack, the R programming language, column-oriented databases and the art of examining data sets for useful patterns. The original blog will be kept alive with Java-specific posts. Wayne is a DZone MVB and is not an employee of DZone and has posted 35 posts at DZone. You can read more from them at their website. View Full User Profile

Java Profiling: Under the Covers

12.01.2009
| 29697 views |
  • submit to reddit

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

  1. How will you launch it?

  2. 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.

Published at DZone with permission of Wayne Adams, author and DZone MVB.

(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

If you do not want to pay money for profiling tool, there is a free and quite suitable profiler – Netbeans profiler

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.