Table of Contents
1. What is Java agent?
In Java 1.5 Instrumentation API was introduced (java.lang.instrument
package) to enable byte-code modification (instrumentation) of classes and to get runtime insights. This is very helpful to tool developers. In this article, we’ll see how Instrumentation API can be leveraged to determine approximate object size at runtime.
Java agent is packaged as a plain Jar containing a set of classes having some specific methods. When an application is run with a Java agent, the agent performs the required instrumentations.
2. Starting Java agent
Java agent can be started either statically or dynamically in a running JVM. To start Java agent statically, add the javaagent
option while launching the application:
java -javaagent:myagent.jar org.openapex.samples.misc.instrument.ObjectSize
To start a Java agent in a running JVM, do the following:
VirtualMachine vm = VirtualMachine.attach(jvmPid); // target JVM PID vm.loadAgent(agentFile.getAbsolutePath());
3. Creating Java agent
If you plan to start the Java agent statically, then create a class with the following premain
method. This method is called by the JVM to start the agent.
public class MyInstrumentationAgent { public static void premain(String agentArgs, Instrumentation inst){ System.out.println("Set the instrumentation from premain"); // Attach class transformers } }
In case you plan to start the Java agent dynamically, then create a class with the following agentmain
method. This method is called by the JVM to start the agent.
public class MyInstrumentationAgent { public static void agentmain(String agentArgs, Instrumentation inst){ System.out.println("Set the instrumentation from agentmain"); // Attach class transformers } }
If you want to have the flexibility to start the agent both statically and dynamically, you may define both the methods in the same class.
Now, create a JAR in the usual way using jar
command and ensure the following entries (Premain-class
and Agent-class
) are present in the manifest file.
Premain-Class: org.openapex.samples.misc.instrument.MyInstrumentationAgent Agent-Class: org.openapex.samples.misc.instrument.MyInstrumentationAgent
Create the agent JAR:
jar -cvfm myagent.jar manifest.txt org/openapex/samples/misc/instrument/MyInstrumentationAgent.class
4. Determine Object size
In the previous sections, we have seen what is Java agent and how to create one for instrumentation. Let us see how we can create a Java agent and leverage Instrumentation.getObjectSize(obj)
to determine object size. Define an agent class with both premain()
and agentmain()
methods. Keep the reference of Instrumentation
object and later use it to get the object size.
package org.openapex.samples.misc.instrument; import java.lang.instrument.Instrumentation; public class MyInstrumentationAgent { private static Instrumentation instrumentation; public static void premain(String agentArgs, Instrumentation inst){ instrumentation = inst; System.out.println("Set the instrumentation from premain"); } public static void premain(String agentArgs){ System.out.println("Unable to set instrumentation premain"); } public static void agentmain(String agentArgs, Instrumentation inst){ instrumentation = inst; System.out.println("Set the insttrumentation from agentmain"); } public static void agentmain(String agentArgs){ System.out.println("Unable to set instrumentation agentmain"); } public static long getObjectSize (Object object){ if (instrumentation != null){ return instrumentation.getObjectSize(object); } System.out.println("Unable to determine object size, instrumentation is not available"); return 0; } }
Let us define the application class that will determine object size. In this example, we get the approximate object size of a File
, String
and Integer
.
package org.openapex.samples.misc.instrument; import java.io.File; import java.lang.instrument.Instrumentation; public class ObjectSize { public static void main(String[] args) { File f = new File("data/user.csv"); String hello = "Hello World"; Integer num = Integer.valueOf(100); System.out.println("Size of file object: " + MyInstrumentationAgent.getObjectSize(f)); System.out.println("Size of file string: " + MyInstrumentationAgent.getObjectSize(hello)); System.out.println("Size of file integer: " + MyInstrumentationAgent.getObjectSize(num)); } }
Start the application by adding javaagent
option:
java -javaagent:myagent.jar org.openapex.samples.misc.instrument.ObjectSize
Here is the output from ObjectSize
application:
Size of file object (bytes): 32 Size of string (bytes): 24 Size of integer (bytes): 16
5. Byte-code modification
If you want to modify byte-code from your Java agent, create one or more ClassFileTransformer
and add these to the Instrumentation
object from premain()
or agentmain()
methods.
The ClassFileTransormer
looks like:
public class MyClassTransformer implements ClassFileTransformer{ @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { // Here change the byte code as needed byte[] newbytes = new byte[100]; return newbytes; } }
Add the transformer to the Instrumentation
object:
public static void premain(String agentArgs, Instrumentation inst){ instrumentation = inst; System.out.println("Set the instrumentation from premain"); instrumentation.addTransformer(new MyClassTransformer()); }
6. Conclusion
In this article, we have seen how to create a simple Java agent and use it in an application. The possible uses cases of instrumentation are limitless. The source code, manifest file, agent Jar files can be downloaded from GitHub
Join our list to get instant access to new articles and weekly newsletter.