javaagent 规范
javaagent 使用 jar 文件格式进行打包,通过 jar 文件里的 manifest 清单文件指定被加载的类,用于启动 agent。
命令行接口
要通过命令行接口启动一个 javaagent,需要在启动命令中添加如下选项:
1 | -javaagent:jarpath[=options] |
其中 jarpath 是 javaagent 文件的路径,options 是 javaagent 的参数。这些配置可以在命令行中出现多次,这样就可以创建多个 agent。多个 agent 可以使用相同的 jarpath。agent jar 文件必须符合 jar 文件规范。
javaagent jar 文件的 manifest 清单中必须包含 Premain-Class
属性,该属性的值是 agent 类的名字。agent 类必须实现一个 public static premain
方法作为 agent 的入口。JVM 在初始化后,会按照 agent 的声明顺序依次调用对应的 premain
方法,然后再调用程序的 main
方法。
premain
方法有两种类型的签名,JVM 会先尝试调用如下签名的方法:
1 | public static void premain(String agentArgs, Instrumentation inst); |
如果 agent 类没有实现上面的方法,JVM 会接着尝试调用如下方法:
1 | public static void premain(String agentArgs); |
agent 类还可以实现 agentmain
方法,用于在 JVM 启动后开启 agent。当 agent 通过命令行方式启动时,agentmain
方法不会执行。
agent 类由系统类加载器进行加载,这也是 main
方法所属类的加载器,premain
方法会和 main
方法运行在同样的安全机制和类加载规则下。任何 main
方法可以做的事,对于 premain
来说都是合法的,包括创建线程。
javaagent 通过 agentArgs
参数传递 agent 选项,该参数作为 String 类型传递,agent 自己负责解析参数内容。
如果 agent 无法正常解析(例如 agent 类无法被加载,或者 agent 类没有合适的 premain
方法),则 JVM 会直接退出。此外,如果 premain
方法抛出了一个未捕获的异常,JVM 也会退出。
在 JVM 启动后启动 agent
还有一种机制,在 JVM 启动后的某个时机启动 javaagent。具体如何初始化的细节和具体的实现有关,通常情况下应用程序已经启动,JVM 已经调用过 main
方法。要支持这种方式,需要满足以下条件:
- Jar 文件的 manifest 清单中需包含
Agent-Class
属性,该属性的值为 agent 类的名字。 - agent 类必须实现一个 public static
agentmain
方法。 - 系统类加载需提供一种机制,用于将 agent jar 文件加载到系统类路径中。
在 JVM 启动并调用完应用程序的 main
方法后,它会尝试调用 agentmain
方法。JVM 首先会尝试调用 agent 类里如下签名的方法:
1 | public static void agentmain(String agentArgs, Instrumentation inst); |
如果未实现上面的方法,JVM 则会尝试调用如下方法:
1 | public static void agentmain(String agentArgs); |
agent 类同样可以实现 premain
方法,用于通过命令行启动。若 agent 在 JVM 启动后再执行,则不会调用 premain
方法。
同样 agentArgs
参数需要 agent 自己解析并处理。
agentmain
方法需要完成启动 agent 所必须的初始化工作,在启动完成后该方法会退出。如果 agent 无法启动,则 JVM 会直接退出。若 agentmain
方法抛出未捕获的异常,JVM 也会直接退出。
Manifest 属性
如下是 agent jar 文件支持的 manifest 属性:
Premain-Class
通过命令行启动时,用于指定 agent 类,值为 agent 类的名字,该 agent 类中需包含
premain
方法Agent-Class
在 JVM 虚拟机启动后再启动 agent,用于指定 agent 类,值为 agent 类的名字,该 agent 类中需包含
agentmain
方法Boot-Class-Path
指定启动类加载器搜索的路径列表。
Can-Redefine-Classes
可选参数,值为 true 或 false。默认值 false。agent 是否需要 redefine 类的能力。
Can-Retransform-Classes
可选参数,值为 true 或 false。默认值 false。agent 是否需要 retransform 类的能力。
Can-Set-Native-Method-Prefix
可选参数,值为 true 或 false。默认值 false。agent 是否需要设置 native 方法前缀的能力。
- 2020-12-04
Rpc Agent is a framework, with which you can develop an agent server for a RPC framework.
- 2019-11-12
javaagent 可以在 main 方法执行前执行一些操作,比如增加一些特殊启动逻辑、在类完成加载前对类的字节码进行修改等。本文不谈底层原理,只介绍如何从零手写一个javaagent。