Understanding Java Agent: Premain and Agentmain Modes with Instrumentation
This article explains the concept of Java Agent, compares it with AOP, and provides step‑by‑step implementations of both premain and agentmain modes, including Maven packaging, VM attachment, class transformation, redefinition, retransformation, and a Javassist example for method timing.
Java Agent, introduced in JDK 1.5, is a technique similar to AOP that allows developers to weave logic at the JVM level for monitoring, running, or replacing programs, and is widely used in profiling, hot‑deployment, and instrumentation scenarios.
Compared with AOP, an agent operates at the virtual‑machine level, requires two separate projects (the agent and the target application), and can be executed in two ways: the premain method before the main program starts (available since JDK 1.5) and the agentmain method after the JVM has started (available since JDK 1.6).
In premain mode, a simple agent class such as public class MyPreMainAgent { public static void premain(String agentArgs, Instrumentation inst) { System.out.println("premain start"); System.out.println("args:" + agentArgs); } } is packaged into a JAR with the required Premain-Class manifest entry using the Maven jar plugin, and the application is launched with the -javaagent:myAgent.jar option; multiple agents can be chained by specifying several -javaagent arguments.
Agentmain mode uses the attach API. After the target program is running (e.g., a class with a blocking System.in.read() ), the following code attaches the agent: VirtualMachine vm = VirtualMachine.attach("16392"); vm.loadAgent("F:/Workspace/MyAgent/target/myAgent.jar", "param"); The agent class implements a public static void agentmain(String agentArgs, Instrumentation inst) method, allowing dynamic loading without restarting the JVM.
The Instrumentation interface provides powerful operations: addTransformer to intercept class loading, redefineClasses to replace a class’s bytecode before it is used, and retransformClasses to modify already loaded classes. Example transformers replace the implementation of a Fruit.getFruit() method with a version that prints "banana" instead of "apple".
Using Javassist simplifies bytecode manipulation. A transformer can copy the original method, rename it, and inject timing logic: CtMethod ctMethod = ctClass.getDeclaredMethod("getFruit"); ctMethod.setName("getFruit$agent"); CtMethod copy = CtNewMethod.copy(ctMethod, ctClass, new ClassMap()); copy.setBody("{ long begin = System.nanoTime(); getFruit$agent($$); System.out.println(\"use \"+(System.nanoTime()-begin)+\" ns\"); }"); ctClass.addMethod(copy); This adds a new method that measures execution time while delegating to the original implementation.
Additional Instrumentation utilities such as removeTransformer , getAllLoadedClasses , and class‑loader search extensions further extend the agent’s capabilities, making Java Agent a versatile tool for advanced backend development.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.