作者:丁仪 来源:https://chengxuzhixin.com/blog/post/Javaagent-zen-me-zhong-xie-zi-jie-ma.html
Java 自从 JDK 1.5 开始提供了 Instrument 机制,允许使用单独的 agent 获取 JVM 信息、动态修改 class 字节码,可以实现无侵入的运行时 AOP。使用 Java agent 可以在 JVM 启动前(JDK 1.5+)或启动后(JDK 1.6+)修改字节码,实现运行时数据的采集和回放(如 doom),也可以用于实时查看线上运行情况(如 arthas)。
启动前加载 agent
启动前加载 agent 可以在 Java 启动命令中,加入 javaagent 选项来启动代理。命令格式是:
-javaagent:jarpath [=options]
其中,jarpath 是代理 jar 文件的路径,options是传给代理的入参数据。javaagent 命令可以使用多次从而创建多个代理,且多个代理可以使用相同的 jarpath。
用于代理的 jar 文件需要满足如下要求:
- manifest 文件中必须指定 Premain-Class 属性,属性的值是代理类的全路径名称;
- 代理类必须实现一个 premain 方法。
premain 方法有两个可用的定义, JVM 首先尝试调用:
public static void premain(String agentArgs, Instrumentation inst);
如果该方法未实现,JVM 再次尝试调用:
public static void premain(String agentArgs);
在 options 配置的入参,会通过 agentArgs 参数传入,由开发者自行解析字符串的内容。如果使用 javaagent 命令创建了多个代理,JVM 会依次调用每个代理的 premain 方法,然后才会调用 main 方法。所以 premain 方法必须返回,否则将无法启动。
代理类会使用系统类加载器( ClassLoader.getSystemClassLoader)来加载,因此 premain 会和 main 方法拥有相同的安全和加载器规则。JVM 不限制 premain 的实现,main 方法能做的事情,premain 都可以做。
启动后加载 agent
除了 premain 方法,JVM(1.6 及以后)还支持在启动之后启动代理。用于代理的 jar 文件需要满足如下要求:
- manifest 文件中必须指定 Agent-Class 属性,属性的值是代理类的全路径名称;
- 代理类必须实现一个 public static 类型的 agentmain 方法;
- 系统类加载器( ClassLoader.getSystemClassLoader)必须支持将代理 jar 文件添加到系统类路径的机制。
agentmain 方法有两个可用的定义, JVM 首先尝试调用:
public static void agentmain(String agentA