虚拟机数量

Load-Time Instrumentation 只涉及到一个虚拟机:

Dynamic Instrumentation 涉及到两个虚拟机:

时机不同

在进行 Load-Time Instrumentation 时,会执行 Agent Jar 当中的 premain() 方法;premain() 方法是先于 main() 方法执行,此时 Application 当中使用的大多数类还没有被加载

在进行 Dynamic Instrumentation 时,会执行 Agent Jar 当中的 agentmain() 方法;而 agentmain() 方法是往往是在 main() 方法之后执行,此时 Application 当中使用的大多数类已经被加载

时机不同

能力不同

Load-Time Instrumentation 可以做很多事情:添加和删除字段、添加和删除方法等。

Dynamic Instrumentation 做的事情比较有限,大多集中在对于方法体的修改。

线程不同

Load-Time Instrumentation 是运行在 main 线程里:

Thread Id: main@1(false)
package lsieun.agent;

import lsieun.utils.*;

import java.lang.instrument.Instrumentation;

public class LoadTimeAgent {
    public static void premain(String agentArgs, Instrumentation inst) {
        // 第一步,打印信息:agentArgs, inst, classloader, thread
        PrintUtils.printAgentInfo(LoadTimeAgent.class, "Premain-Class", agentArgs, inst);
    }
}

Dynamic Instrumentation 是运行在 Attach Listener 线程里:

Thread Id: Attach Listener@5(true)
package lsieun.agent;

import lsieun.utils.*;

import java.lang.instrument.Instrumentation;

public class DynamicAgent {
    public static void agentmain(String agentArgs, Instrumentation inst) {
        // 第一步,打印信息:agentArgs, inst, classloader, thread
        PrintUtils.printAgentInfo(DynamicAgent.class, "Agent-Class", agentArgs, inst);
    }
}

Exception 处理

在处理 Exception 的时候,Load-Time Instrumentation 和 Dynamic Instrumentation 有差异: Link

  • 当 Load-Time Instrumentation 时,出现异常,会报告错误信息,并且停止执行,退出虚拟机。
  • 当 Dynamic Instrumentation 时,出现异常,会报告错误信息,但是不会停止虚拟机,而是继续执行。

在 Load-Time Instrumentation 过程中,遇到异常:

  • If the agent cannot be resolved (for example, because the agent class cannot be loaded, or because the agent class does not have an appropriate premain method), the JVM will abort.
  • If a premain method throws an uncaught exception, the JVM will abort.

在 Dynamic Instrumentation 过程中,遇到异常:

  • If the agent cannot be started (for example, because the agent class cannot be loaded, or because the agent class does not have a conformant agentmain method), the JVM will not abort.
  • If the agentmain method throws an uncaught exception, it will be ignored.

Agent Class 不存在

Load-Time Agent

pom.xml 中,修改 Premain-Class 属性,指向一个不存在的 Agent Class:

<Premain-Class>lsieun.agent.NonExistentAgent</Premain-Class>

会出现 ClassNotFoundException 异常:

$ java -cp ./target/classes/ -javaagent:./target/TheAgent.jar sample.Program
Exception in thread "main" java.lang.ClassNotFoundException: lsieun.agent.NonExistentAgent
        at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
        at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:304)
        at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:401)
FATAL ERROR in native method: processing of -javaagent failed

Dynamic Agent

pom.xml 中,修改 Agent-Class 属性,指向一个不存在的 Agent Class:

<Agent-Class>lsieun.agent.NonExistentAgent</Agent-Class>

会出现 ClassNotFoundException 异常:

Exception in thread "Attach Listener" java.lang.ClassNotFoundException: lsieun.agent.NonExistentAgent
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:304)
	at sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:411)
Agent failed to start!

incompatible xxx-main

Load-Time Agent

如果 premain 方法不符合规范:

public class LoadTimeAgent {
    public static void premain() {
        // do nothing
    }
}

会出现 NoSuchMethodException 异常:

$ java -cp ./target/classes/ -javaagent:./target/TheAgent.jar sample.Program
Exception in thread "main" java.lang.NoSuchMethodException: lsieun.agent.LoadTimeAgent.premain(java.lang.String, java.lang.instrument.Instrumentation)
        at java.lang.Class.getDeclaredMethod(Class.java:2130)
        at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:327)
        at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:401)
FATAL ERROR in native method: processing of -javaagent failed

Dynamic Agent

如果 agentmain 方法不符合规范:

public class DynamicAgent {
    public static void agentmain() {
        // do nothing
    }
}

会出现 NoSuchMethodException 异常:

Exception in thread "Attach Listener" java.lang.NoSuchMethodException: lsieun.agent.DynamicAgent.agentmain(java.lang.String, java.lang.instrument.Instrumentation)
	at java.lang.Class.getDeclaredMethod(Class.java:2130)
	at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:327)
	at sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:411)
Agent failed to start!

抛出异常

Load-Time Agent

import java.lang.instrument.Instrumentation;

public class LoadTimeAgent {
    public static void premain(String agentArgs, Instrumentation inst) {
        throw new RuntimeException("exception from LoadTimeAgent");
    }
}
$ java -cp ./target/classes/ -javaagent:./target/TheAgent.jar sample.Program
Exception in thread "main" java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:386)
        at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:401)
Caused by: java.lang.RuntimeException: exception from LoadTimeAgent
        at lsieun.agent.LoadTimeAgent.premain(LoadTimeAgent.java:7)
        ... 6 more
FATAL ERROR in native method: processing of -javaagent failed

Dynamic Agent

import java.lang.instrument.Instrumentation;

public class DynamicAgent {
    public static void agentmain(String agentArgs, Instrumentation inst) {
        throw new RuntimeException("exception from DynamicAgent");
    }
}
Exception in thread "Attach Listener" java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:386)
	at sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:411)
Caused by: java.lang.RuntimeException: exception from DynamicAgent
	at lsieun.agent.DynamicAgent.agentmain(DynamicAgent.java:7)
	... 6 more
Agent failed to start!

总结

本文内容总结如下:

  • 第一点,虚拟机的数量不同。
  • 第二点,时机不同和能力不同。
  • 第三点,线程不同。
  • 第四点,处理异常的方式不同。