I was intrigued by
this blog, referenced by
Eamonn, so I decided to take a look at the "attach on demand" feature.
Since J2SE 5, it is possible to instrument a JVM using an "agent" with the following:
java -javaagent:<jarpath>[=<options>] <mainclass>
where
<jarpath>
is the path to the jar file containing the agent implementation (absolute, or relative to the directory from where the JVM has been started).
The option can be present multiple times, thus instantiating multiple agents.
The jar's manifest must contain an entry called
Premain-Class
that specifies the full qualified name of the agent implementation class.
A side effect of invoking the JVM with the
-javaagent
option is that the jar specified by
<jarpath>
is added to the system classpath.
Agent implementations must have a method called
premain
that is called by the JVM before the method
main
in
<mainclass>
is called.
In J2SE 5, the signature of
premain()
must be:
public static void premain(String options, Instrumentation inst)
In J2SE 6 the signature can only be:
public static void premain(String options)
That is, if the
instrumentation implementation is not needed, you can drop it from the signature.
In J2SE 6, the agent mechanism has been extended so it is possible to invoke agents
after the JVM has been started, and not only at JVM startup using the
-javaagent
option.
The mechanism is slightly different, and requires use of
com.sun.*
classes, so it's specific to Sun's (and derived) JVMs.
Let's take a look at how it works.
First, we need a reference to the JVMs we want to attach to. This is accomplished with the following code (classes are from
com.sun.tools.attach.*
package, shipped in
$J2SE6/lib/tools.jar
):
List<? extends VirtualMachineDescriptor> jvms = VirtualMachine.list();
Then it's possible to attach to a JVM using:
VirtualMachineDescriptor vmd = ...;
VirtualMachine jvm = VirtualMachine.attach(vmd);
Once we're attached, we can invoke the agent mechanism using:
String jarPath = ...;
String options = ...;
jvm.loadAgent(jarPath, options);
Once the agent is loaded in the target JVM, we can detach from it:
jvm.detach();
Loading the agent with this mechanism has the same side effect of adding the jar specified by
jarPath
to the system classpath.
The jar's manifest file must contain, this time, and entry called
Agent-Class
specifying the full qualified name of the agent implementation class.
However, the agent implementation must be slightly different.
Instead of calling the
premain
method, the JVM calls this method:
public static void agentmain(String options, Instrumentation inst)
Again, if you don't need it, you can drop the
Instrumentation
argument from the signature.
Inside
agentmain()
it's possible to write any code (with the same restrictions of
premain()
), so it's basically possible to do whatever one wants.
For example, using this mechanism in Mustang allows to do, in any moment, the following:
- add a jar to the system classpath
- be able to invoke a method of an arbitrary class (the agent)
It is possible to write a completely empty implementation of
agentmain()
just to be able to load new jars on demand.
Another use is to be able to "hot fix" a running JVM, using the
java.lang.instrument.Instrumentation
implementation to replace a malfunctioning class (with the restrictions imposed by the instrumentation mechanism).
Or can be used to tell to the target JVM to collect information about what is doing and dump it somewhere for statistical/monitoring purposes.
In Mustang there is an example of this mechanism to start a
JMXConnectorServer, so that the JVM becomes remotely manageable. The jar is shipped in
$J2SE6/jre/lib/management-agent.jar
and it's completely empty (apart the manifest file) since the agent implementation is shipped in
rt.jar
(it's the
sun.management.Agent
class).
Feel free to drop a comment if you have other cool ideas of how to use this mechanism !