包 java.lang.instrument


java.lang.instrument
提供允许 Java 编程语言代理检测在 JVM 上运行的程序的服务。检测机制是修改方法的字节码。

代理部署为 JAR 文件。 JAR 文件清单中的属性指定将加载以启动代理的代理类。可以通过多种方式启动代理:

  1. 对于支持命令行界面的实现,可以通过在命令行上指定一个选项来启动代理。

  2. 实现可能支持在 VM 启动后的某个时间启动代理的机制。例如,一个实现可以提供一种机制,允许工具到正在运行的应用程序,并开始将该工具的代理加载到正在运行的应用程序中。

  3. 代理可以与应用程序一起打包在可执行 JAR 文件中。

代理可以在加载时以任意方式转换类、转换模块或转换已加载类的方法的字节码。部署代理、部署将代理与应用程序打包的应用程序或使用将代理加载到正在运行的应用程序的工具的开发人员或管理员负责验证每个代理的可信度,包括代理 JAR 文件的内容和结构。

下面介绍三种启动代理的方式。

从命令行界面启动代理

如果实现提供了从命令行界面启动代理的方法,则通过向命令行添加以下选项来启动代理:

-javaagent:<jarpath>[=<options>]
在哪里<jarpath> 是代理 JAR 文件的路径,并且<options> 是代理选项。

代理 JAR 文件的清单必须在其主清单中包含属性 Premain-Class。该属性的值是代理类.代理类必须实现一个公共静态 premain 方法,原则上类似于 main 应用程序入口点。 Java 虚拟机 (JVM) 初始化后,premain 方法将被调用,然后才是真正的应用程序 main 方法。 premain 方法必须返回才能继续启动。

premain 方法具有两种可能的签名之一。 JVM 首先尝试在代理类上调用以下方法:

public static void premain(String agentArgs, Instrumentation inst)

如果代理类未实现此方法,则 JVM 将尝试调用:

public static void premain(String agentArgs)

代理类也可能有一个 agentmain 方法,用于在 VM 启动后启动代理时使用(见下文)。使用命令行选项启动代理时,不会调用 agentmain 方法。

每个代理都通过 agentArgs 参数传递其代理选项。代理选项作为单个字符串传递,任何额外的解析都应由代理本身执行。

如果无法启动代理(例如,因为无法加载代理类,或者因为代理类没有合适的premain方法),JVM 将中止。如果 premain 方法抛出未捕获的异常,JVM 将中止。

不需要实现来提供从命令行界面启动代理的方法。当它这样做时,它就会支持上面指定的 -javaagent 选项。 -javaagent 选项可以在同一命令行上多次使用,从而启动多个代理。 premain 方法将按照在命令行中指定代理的顺序调用。不止一个代理可以使用相同的<jarpath> .

代理premain 方法可以做什么没有建模限制。应用程序 main 可以做的任何事情,包括创建线程,从 premain 开始都是合法的。

VM启动后启动Agent

一个实现可能会提供一种机制来在 VM 启动后的某个时间启动代理。有关如何启动的详细信息是特定于实现的,但通常应用程序已经启动并且其 main 方法已被调用。如果实现支持在 VM 启动后启动代理,则适用以下条件:

  1. 代理 JAR 的清单必须在其主要清单中包含属性 Agent-Class。该属性的值是代理类.

  2. 代理类必须实现公共静态 agentmain 方法。

agentmain 方法具有两种可能的签名之一。 JVM 首先尝试在代理类上调用以下方法:

public static void agentmain(String agentArgs, Instrumentation inst)

如果代理类未实现此方法,则 JVM 将尝试调用:

public static void agentmain(String agentArgs)

代理类也可能有一个 premain 方法,用于在使用命令行选项启动代理时使用。在 VM 启动后启动代理时,不会调用 premain 方法。

代理通过 agentArgs 参数传递其代理选项。代理选项作为单个字符串传递,任何额外的解析都应由代理本身执行。

agentmain 方法应执行启动代理所需的任何必要初始化。启动完成后,该方法应返回。如果无法启动代理(例如,因为无法加载代理类,或者因为代理类没有符合要求的agentmain方法),JVM 不会中止。如果 agentmain 方法抛出一个未捕获的异常,它将被忽略(但可能会被 JVM 记录下来以进行故障排除)。

在可执行 JAR 文件中包含代理

JAR 文件规范为打包为 executable JAR files 的独立应用程序定义清单属性。如果实现支持将应用程序作为可执行 JAR 启动的机制,则主清单可能包含 Launcher-Agent-Class 属性以指定在调用应用程序 main 方法之前启动的代理的类名。 Java 虚拟机尝试在代理类上调用以下方法:

public static void agentmain(String agentArgs, Instrumentation inst)

如果代理类未实现此方法,则 JVM 将尝试调用:

public static void agentmain(String agentArgs)

agentArgs 参数的值始终为空字符串。

agentmain 方法应执行启动代理所需的任何必要初始化并返回。如果agent无法启动,例如agent类无法加载,agent类没有定义符合的agentmain方法,或者agentmain方法抛出未捕获的异常或错误,JVM会中止。

加载代理类和代理类可用的模块/类

从代理 JAR 文件加载的类由 系统类加载器 加载,并且是系统类加载器的 未命名模块 的成员。系统类加载器通常也定义包含应用程序 main 方法的类。

代理类可见的类是系统类加载器可见的类,至少包括:

  • 引导层 中的模块导出的包中的类。引导层是否包含所有平台模块将取决于初始模块或应用程序的启动方式。

  • 可以由系统类加载器(通常是类路径)定义为未命名模块成员的类。

  • 代理安排由引导类加载器定义的任何类是其未命名模块的成员。

如果代理类需要链接到不在引导层中的平台(或其他)模块中的类,那么应用程序可能需要以确保这些模块在引导层中的方式启动。例如,在 JDK 实现中,--add-modules 命令行选项可用于将模块添加到根模块集中以在启动时解析。

代理安排由引导类加载器加载的支持类(通过 appendToBootstrapClassLoaderSearch 或下面指定的 Boot-Class-Path 属性)必须仅链接到定义到引导类加载器的类。不能保证所有平台类都可以由引导类加载器定义。

如果配置了自定义系统类加载器(通过 getSystemClassLoader 方法中指定的系统属性 java.system.class.loader),则它必须定义 appendToSystemClassLoaderSearch 中指定的 appendToClassPathForInstrumentation 方法。换句话说,自定义系统类加载器必须支持将代理 JAR 文件添加到系统类加载器搜索的机制。

清单属性

为代理 JAR 文件定义了以下清单属性:

Premain-Class
当在 JVM 启动时指定代理时,此属性指定代理类。即,包含 premain 方法的类。当在 JVM 启动时指定代理程序时,此属性是必需的。如果该属性不存在,JVM 将中止。注意:这是一个类名,而不是文件名或路径。
Agent-Class
如果实现支持在 VM 启动后的某个时间启动代理的机制,则此属性指定代理类。即,包含 agentmain 方法的类。如果该属性不存在,代理将不会启动,则该属性是必需的。注意:这是一个类名,而不是文件名或路径。
Launcher-Agent-Class
如果实现支持将应用程序作为可执行 JAR 启动的机制,则主清单可能包含此属性以指定在调用应用程序 main 方法之前启动的代理的类名。
Boot-Class-Path
引导类加载器要搜索的路径列表。路径表示目录或库(在许多平台上通常称为 JAR 或 zip 库)。在定位类的平台特定机制失败后,引导类加载器将搜索这些路径。按照列出的顺序搜索路径。列表中的路径由一个或多个空格分隔。路径采用分层 URI 的路径组件的语法。如果路径以斜杠字符 ('/') 开头,则它是绝对路径,否则它是相对的。相对路径根据代理 JAR 文件的绝对路径进行解析。格式错误和不存在的路径将被忽略。当代理在 VM 启动后的某个时间启动时,不代表 JAR 文件的路径将被忽略。该属性是可选的。
Can-Redefine-Classes
boolean(truefalse,大小写无关)。是否能够重新定义此代理所需的类。 true 以外的值被视为 false 。该属性是可选的,默认为 false
Can-Retransform-Classes
boolean(truefalse,大小写无关)。是重新转换此代理所需的类的能力。 true 以外的值被视为 false 。该属性是可选的,默认为 false
Can-Set-Native-Method-Prefix
boolean(truefalse,大小写无关)。是否能够设置此代理所需的本机方法前缀。 true 以外的值被视为 false 。该属性是可选的,默认为 false

代理 JAR 文件可能同时具有清单中的 Premain-Class Agent-Class 属性。当使用 -javaagent 选项在命令行上启动代理时, Premain-Class 属性指定代理类的名称,而 Agent-Class 属性将被忽略。同样,如果代理在 VM 启动后的某个时间启动,则 Agent-Class 属性指定代理类的名称(忽略 Premain-Class 属性的值)。

检测模块中的代码

作为在引导类加载器的搜索路径或加载主代理类的类加载器的搜索路径上部署支持类的代理的辅助,Java虚拟机安排转换类的模块读取未命名模块两个类加载器。

自从:
1.5