What is Java Instrumentation? Why is it needed?

Mary Reni
Javarevisited
Published in
4 min readApr 6, 2021

A simple explanation with code snippets

Disclaimer: This is an oversimplified explanation. Please ignore minor oversights, thank you.

So, what is Java instrumentation?

You might have read that it is a way to add extra byte code to your application byte code. If you want to add something to your application while it is executed, without actually touching the application code, then instrumentation is what you want to do.

So why exactly is it significant, and who uses it?

Java instrumentation is used mostly by Application Tools like jaCoCO, Profilers, and Monitoring tools like Datadog, Instana, etc. These tools need to attach themselves to the application code, without actually touching the application code, because, well, they do not have your application code to add their functionalities to it.

Imagine like this, if 10 application teams are developing 10 different microservices, and if they need to extract the exact simple metrics from those microservices without going for a Monitoring tool, then they can write simple agents to extract these metrics alone. If it gets complex, then it is easier to go for a Monitoring tool than writing specific agent code.

So, is it very complex that only experts can do the instrumentation? Definite No.

Does that mean you should also do it for your application? Also No.

Most of the time, Application developers do not need to worry about instrumentation, unless you have very specific enterprise-level needs to update application codes in order to extract certain metrics (or some other profiling or whatever is needed). This is almost never the case.

But it is always fun to learn about how tools work, to develop some tools by ourselves!

So, let us see how it works with a very simple example. Before going into details, I would like to note here that there are two ways on how instrumentation is done.

  1. Add agent code while Classloader loads the application during JVM launch
  2. Add agent code after the application is started

I have taken option 1 for explaining here.

For application code, let us have a simple java class that does nothing but print the current time and exits.

Let us compile this and bundle it into a jar with a manifest file, let's name it application.mf

Main-Class: ApplicationBeingTransformed

For compiling and bundling this simple application code into a jar, execute the below

javac ApplicationBeingTransformed.javajar cfm simpleApplication.jar application.mf ApplicationBeingTransformed.class

Now, we have a jar called simpleApplication.jar.

We will now write a simple agent class, to add a footer when methods in the ApplicationBeingTransformed class exit. I am going to use ByteBuddy here to avoid boilerplate code. ByteBuddy is an outstanding library to work with Instrumentation.

In the above code, what I have done is

  1. Create premain method, which is the entry point for agents launched during jvm-launch
  2. Creating a simple Advice class to add a SysOut statement (line number 31) to the end of the method code.
  3. Wire the advice class to instrumentation class as a transformer in line number 24
  4. It would do this transformation when the element name has “ApplicationBeingTransformed” in line number 18
  5. This advice would be visited only when it is a method. Method is also an element which would have the name “ApplicationBeingTransformed” in it.

Let us compile this with javac and bundle this into jar with a manifest file.

javac -cp byte-buddy-1.10.22.jar;. SimpleInstru.javajar cfm simpleInstrumentation.jar instru.mf SimpleInstru.class SimpleInstru\$TimeFooter.class

We now have a agent jar called simpleInstrumentation.jar.

One important thing to note in an agent code which is launched during jvm-launch, is to add Pre-main class name in the manifest file. Below is the manifest file I have used:

Premain-Class: SimpleInstru
Class-Path: byte-buddy-1.10.22.jar

Now, so far, what have we done?

  1. we have created an application
  2. we have created an agent to add an extra line of code to the application code

Let us now execute and check how it looks. We have two jars, simpleApplication.jar and simpleInstrumentation.jar

I have not used any complex packaging or build tools. So I am going to need the byteBuddy jar in the directory from where I am going execute(as per the manifest file I have declared).

Command for executing the agent alongside the application code.

java -javaagent:simpleInstrumentation.jar -jar simpleApplication.jar

Now, the extra line of code from the agent is inserted into the application code. We can see it from the execution as below:

If I have another method in ApplicationBeingTransformed.java, this footer would be added there as well.

So, whenever methods in this class are called, the footer message would also be printed in the console, as this footer is now part of the application byte-code.

Code used here is available in Github: Author’s repository

--

--