-
public interface Instrumentation
此类提供了检测Java编程语言代码所需的服务。 仪表是向方法添加字节代码,用于收集工具使用的数据。 由于这些更改纯粹是附加的,因此这些工具不会修改应用程序状态或行为。 此类良性工具的示例包括监视代理程序,分析程序,覆盖分析程序和事件记录程序。有两种方法可以获取
Instrumentation
接口的实例:以指示代理类的方式启动JVM时。 在这种情况下,
Instrumentation
实例将传递给代理类的premain
方法。当JVM在JVM启动后的某个时间提供启动代理的机制时。 在这种情况下,
Instrumentation
实例将传递给代理程序代码的agentmain
方法。
这些机制在package specification中描述。
代理获取
Instrumentation
实例后,代理可以随时调用实例上的方法。- 从以下版本开始:
- 1.5
-
-
方法摘要
所有方法 实例方法 抽象方法 变量和类型 方法 描述 void
addTransformer(ClassFileTransformer transformer)
注册随附的变压器。void
addTransformer(ClassFileTransformer transformer, boolean canRetransform)
注册随附的变压器。void
appendToBootstrapClassLoaderSearch(JarFile jarfile)
指定一个JAR文件,其中包含由引导类加载器定义的检测类。void
appendToSystemClassLoaderSearch(JarFile jarfile)
指定一个JAR文件,其中包含由系统类加载器定义的检测类。类[]
getAllLoadedClasses()
返回JVM当前加载的所有类的数组。类[]
getInitiatedClasses(ClassLoader loader)
返回loader
作为启动加载器的所有类的数组。long
getObjectSize(Object objectToSize)
返回指定对象占用的存储量的特定于实现的近似值。boolean
isModifiableClass(类<?> theClass)
测试类是否可由 retransformation或 redefinition修改。boolean
isModifiableModule(模块 module)
测试是否可以使用redefineModule
修改模块。boolean
isNativeMethodPrefixSupported()
返回当前JVM配置是否支持 setting a native method prefix 。boolean
isRedefineClassesSupported()
返回当前JVM配置是否支持重新定义类。boolean
isRetransformClassesSupported()
返回当前JVM配置是否支持类的重新转换。void
redefineClasses(ClassDefinition... definitions)
使用提供的类文件重新定义提供的类集。void
redefineModule(模块 module, Set<模块> extraReads, Map<String,Set<模块>> extraExports, Map<String,Set<模块>> extraOpens, Set<类<?>> extraUses, Map<类<?>,List<类<?>>> extraProvides)
重新定义模块以展开它读取的模块集,它导出或打开的软件包集,或它使用或提供的服务。boolean
removeTransformer(ClassFileTransformer transformer)
取消注册提供的变压器。void
retransformClasses(类<?>... classes)
重新转换提供的类集。void
setNativeMethodPrefix(ClassFileTransformer transformer, String prefix)
此方法通过允许使用应用于名称的前缀进行重试来修改本机方法解析的失败处理。
-
-
-
方法详细信息
-
addTransformer
void addTransformer(ClassFileTransformer transformer, boolean canRetransform)
注册随附的变压器。 除了任何注册变压器所依赖的类的定义之外,变压器将看到所有未来的类定义。 加载类时调用变换器,当它们为redefined时 。 如果canRetransform
为真, 则为retransformed 。ClassFileTransformer
定义转换调用的顺序。 如果变压器在执行期间抛出异常,JVM仍将按顺序调用其他已注册的变换器。 可以多次添加相同的变压器,但强烈建议不要这样做 - 通过创建变压器类的新实例来避免这种情况。该方法旨在用于仪器,如class specification中所述。
- 参数
-
transformer
- 要注册的变压器 -
canRetransform
- 可以重新转换此变换器的转换 - 异常
-
NullPointerException
- 如果通过null
变压器 -
UnsupportedOperationException
- 如果canRetransform
为true且JVM的当前配置不允许重新转换(isRetransformClassesSupported()
为false) - 从以下版本开始:
- 1.6
-
addTransformer
void addTransformer(ClassFileTransformer transformer)
注册随附的变压器。与
addTransformer(transformer, false)
相同。- 参数
-
transformer
- 要注册的变压器 - 异常
-
NullPointerException
- 如果通过null
变压器 - 另请参见:
-
addTransformer(ClassFileTransformer,boolean)
-
removeTransformer
boolean removeTransformer(ClassFileTransformer transformer)
取消注册提供的变压器。 未来的类定义将不会显示给变换器。 删除最近添加的变换器匹配实例。 由于类加载的多线程特性,变换器可以在移除后接收呼叫。 变形金刚应该采取防御措施来预期这种情况。- 参数
-
transformer
- 要取消注册的变压器 - 结果
- 如果找到并移除变压器,则为true;如果未找到变压器,则为false
- 异常
-
NullPointerException
- 如果通过null
变压器
-
isRetransformClassesSupported
boolean isRetransformClassesSupported()
返回当前JVM配置是否支持类的重新转换。 重新转换已加载的类的能力是JVM的可选功能。 仅当代理JAR文件中的Can-Retransform-Classes
清单属性设置为true
(如package specification中所述)且JVM支持此功能时,才支持重新转换。 在单个JVM的单个实例化期间,对此方法的多次调用将始终返回相同的答案。- 结果
- 如果当前JVM配置支持类的重新转换,则返回true,否则返回false。
- 从以下版本开始:
- 1.6
- 另请参见:
-
retransformClasses(java.lang.Class<?>...)
-
retransformClasses
void retransformClasses(类<?>... classes) throws UnmodifiableClassException
重新转换提供的类集。此函数有助于已加载类的检测。 当最初加载类或者它们是redefined时 ,可以使用
ClassFileTransformer
转换初始类文件字节。 此函数重新运行转换过程(无论先前是否发生过转换)。 此重新转换遵循以下步骤:- 从初始类文件字节开始
- 对于添加了
canRetransform
false的每个转换器,在最后一个类加载或重新定义期间由transform
返回的字节将被重用为转换的输出; 请注意,这相当于重新应用以前的转换,不变; 除了没有调用transform
方法。 - 对于添加了
canRetransform
每个变压器,在这些变压器中调用transform
方法 - 转换后的类文件字节作为类的新定义安装
转换顺序在
ClassFileTransformer
中描述。 这个相同的顺序用于自动重新应用无法重新转换的转换。初始类文件字节表示传递给
ClassLoader.defineClass
或redefineClasses
的字节(在应用任何转换之前),但它们可能与它们不完全匹配。 常量池可能没有相同的布局或内容。 常量池可以具有更多或更少的条目。 常量池条目的顺序可以不同; 但是,方法的字节码中的常量池索引将对应。 某些属性可能不存在。 如果订单没有意义,例如方法的顺序,则可能不会保留订单。该方法对集合进行操作,以便允许同时对多个类进行相互依赖的更改(类A的重新转换可能需要对类B进行重新转换)。
如果重新传输的方法具有活动堆栈帧,则这些活动帧将继续运行原始方法的字节码。 重新转换的方法将用于新的调用。
除了在常规JVM语义下发生的情况之外,此方法不会导致任何初始化。 换句话说,重新定义类不会导致其初始化器运行。 静态变量的值将保持与调用之前的值相同。
重新转换的类的实例不受影响。
重新转换可能会更改方法体,常量池和属性(除非明确禁止)。 重新转换不得添加,删除或重命名字段或方法,更改方法的签名或更改继承。 重新转换不得更改
NestHost
或NestMembers
属性。 在将来的版本中可能会取消这些限制。 在应用转换之前,不会检查,验证和安装类文件字节,如果生成的字节错误,则此方法将引发异常。如果此方法抛出异常,则不会重新转换任何类。
该方法旨在用于仪器,如class specification中所述。
- 参数
-
classes
- 要重新转换的类数组; 允许零长度数组,在这种情况下,此方法不执行任何操作 - 异常
-
UnmodifiableClassException
- 如果指定的类不能修改(isModifiableClass(java.lang.Class<?>)
将返回false
) -
UnsupportedOperationException
- 如果JVM的当前配置不允许重新转换(isRetransformClassesSupported()
为false)或重新转换尝试进行不支持的更改 -
ClassFormatError
- 如果数据不包含有效类 -
NoClassDefFoundError
- 如果类文件中的名称不等于类的名称 -
UnsupportedClassVersionError
- 如果不支持类文件版本号 -
ClassCircularityError
- 如果新类包含循环度 -
LinkageError
- 如果发生链接错误 -
NullPointerException
- 如果提供的类数组或其任何组件是null
。 - 从以下版本开始:
- 1.6
- 另请参见:
-
isRetransformClassesSupported()
,addTransformer(java.lang.instrument.ClassFileTransformer, boolean)
,ClassFileTransformer
-
isRedefineClassesSupported
boolean isRedefineClassesSupported()
返回当前JVM配置是否支持重新定义类。 重新定义已加载的类的能力是JVM的可选功能。 仅当代理JAR文件中的Can-Redefine-Classes
清单属性设置为true
(如package specification中所述)且JVM支持此功能时,才支持重新定义。 在单个JVM的单个实例化期间,对此方法的多次调用将始终返回相同的答案。- 结果
- 如果当前JVM配置支持重新定义类,则返回true,否则返回false。
- 另请参见:
-
redefineClasses(java.lang.instrument.ClassDefinition...)
-
redefineClasses
void redefineClasses(ClassDefinition... definitions) throws ClassNotFoundException, UnmodifiableClassException
使用提供的类文件重新定义提供的类集。此方法用于替换类的定义而不引用现有的类文件字节,就像从源代码重新编译以进行修复并继续调试时所做的那样。 如果要转换现有的类文件字节(例如在字节码检测中),则应使用
retransformClasses
。此方法对集合进行操作,以便允许同时对多个类进行相互依赖的更改(对类A的重新定义可能需要重新定义类B)。
如果重新定义的方法具有活动堆栈帧,则这些活动帧将继续运行原始方法的字节码。 重新定义的方法将用于新的调用。
除了在常规JVM语义下发生的情况之外,此方法不会导致任何初始化。 换句话说,重新定义类不会导致其初始化器运行。 静态变量的值将保持与调用之前的值相同。
重新定义的类的实例不受影响。
重新定义可能会更改方法体,常量池和属性(除非明确禁止)。 重定义不得添加,删除或重命名字段或方法,更改方法的签名或更改继承。 重新定义不得更改
NestHost
或NestMembers
属性。 在将来的版本中可能会取消这些限制。 在应用转换之前,不会检查,验证和安装类文件字节,如果生成的字节错误,则此方法将引发异常。如果此方法抛出异常,则不会重新定义任何类。
该方法旨在用于仪器,如class specification中所述。
- 参数
-
definitions
- 要使用相应定义重新定义的类数组; 允许零长度数组,在这种情况下,此方法不执行任何操作 - 异常
-
UnmodifiableClassException
- 如果指定的类不能修改(isModifiableClass(java.lang.Class<?>)
将返回false
) -
UnsupportedOperationException
- 如果JVM的当前配置不允许重新定义(isRedefineClassesSupported()
为false)或重新定义尝试进行不受支持的更改 -
ClassFormatError
- 如果数据不包含有效类 -
NoClassDefFoundError
- 如果类文件中的名称不等于类的名称 -
UnsupportedClassVersionError
- 如果不支持类文件版本号 -
ClassCircularityError
- 如果新类包含循环度 -
LinkageError
- 如果发生链接错误 -
NullPointerException
- 如果提供的定义数组或其任何组件是null
-
ClassNotFoundException
- 永远不能抛出(仅出于兼容性原因而存在) - 另请参见:
-
isRedefineClassesSupported()
,addTransformer(java.lang.instrument.ClassFileTransformer, boolean)
,ClassFileTransformer
-
isModifiableClass
boolean isModifiableClass(类<?> theClass)
测试类是否可由retransformation或redefinition修改。 如果类是可修改的,则此方法返回true
。 如果类不可修改,则此方法返回false
。对于要重新转换的类,
isRetransformClassesSupported()
也必须为true。 但是isRetransformClassesSupported()
的值不会影响此函数返回的值。 对于要重新定义的类,isRedefineClassesSupported()
也必须为true。 但是isRedefineClassesSupported()
的值不会影响此函数返回的值。原始类(例如,
java.lang.Integer.TYPE
)和数组类永远不可修改。- 参数
-
theClass
- 要检查可修改的类 - 结果
- 参数类是否可修改
- 异常
-
NullPointerException
- 如果指定的类是null
。 - 从以下版本开始:
- 1.6
- 另请参见:
-
retransformClasses(java.lang.Class<?>...)
,isRetransformClassesSupported()
,redefineClasses(java.lang.instrument.ClassDefinition...)
,isRedefineClassesSupported()
-
getAllLoadedClasses
类[] getAllLoadedClasses()
返回JVM当前加载的所有类的数组。- 结果
- 包含JVM加载的所有类的数组,如果没有,则为零长度
-
getInitiatedClasses
类[] getInitiatedClasses(ClassLoader loader)
返回loader
作为启动加载器的所有类的数组。 如果提供的加载程序是null
,则返回由引导程序类加载器启动的类。- 参数
-
loader
- 将返回其已启动的类列表的加载程序 - 结果
- 包含加载器作为启动加载器的所有类的数组,如果没有,则为零长度
-
getObjectSize
long getObjectSize(Object objectToSize)
返回指定对象占用的存储量的特定于实现的近似值。 结果可能包括对象的一些或全部开销,因此可用于实现内的比较,但不适用于实现之间。 在单次调用JVM期间,估计可能会更改。- 参数
-
objectToSize
- 要求大小的对象 - 结果
- 特定于实现的近似指定对象消耗的存储量
- 异常
-
NullPointerException
- 如果提供的对象是null
。
-
appendToBootstrapClassLoaderSearch
void appendToBootstrapClassLoaderSearch(JarFile jarfile)
指定一个JAR文件,其中包含由引导类加载器定义的检测类。当虚拟机的内置类加载器(称为“引导类加载器”)未成功搜索类时,也将搜索
JAR file
中的条目。可以多次使用此方法以按照调用此方法的顺序添加要搜索的多个JAR文件。
代理应注意确保JAR不包含除引导类加载器为了进行检测而定义的类或资源之外的任何类或资源。 不遵守此警告可能导致难以诊断的意外行为。 例如,假设有一个加载器L,并且用于委托的L's parent是引导类加载器。 此外,类C中的方法(由L定义的类)引用非公共访问类C $ 1。 如果JAR文件包含类C $ 1,那么对引导类加载器的委托将导致引导类加载器定义C $ 1。 在此示例中,将抛出可能导致应用程序失败的
IllegalAccessError
。 避免这些类型问题的一种方法是为检测类使用唯一的包名称。The Java™ Virtual Machine Specification指定后续尝试解析Java虚拟机先前未成功尝试解析的符号引用始终失败,并且由于初始解析尝试而引发的错误相同。 因此,如果JAR文件包含与Java虚拟机未成功尝试解析引用的类相对应的条目,则后续尝试解析该引用将失败,并出现与初始尝试相同的错误。
- 参数
-
jarfile
- 引导类加载器未成功搜索类时要搜索的JAR文件。 - 异常
-
NullPointerException
- 如果jarfile
是null
。 - 从以下版本开始:
- 1.6
- 另请参见:
-
appendToSystemClassLoaderSearch(java.util.jar.JarFile)
,ClassLoader
,JarFile
-
appendToSystemClassLoaderSearch
void appendToSystemClassLoaderSearch(JarFile jarfile)
指定一个JAR文件,其中包含由系统类加载器定义的检测类。 当用于委派的系统类加载器(参见getSystemClassLoader()
)未成功搜索类时,也将搜索JarFile
中的条目。可以多次使用此方法以按照调用此方法的顺序添加要搜索的多个JAR文件。
代理应该注意确保JAR不包含除系统类加载器为了检测而定义的类或资源之外的任何类或资源。 未能遵守此警告可能会导致难以诊断的意外行为(请参阅
appendToBootstrapClassLoaderSearch
)。系统类加载器支持添加,如果它实现了一个命名的方法要被搜索的JAR文件
appendToClassPathForInstrumentation
其中采用类型的单个参数java.lang.String
。 该方法不需要具有public
访问权限。 通过调用获得的JAR文件的名称getName()
的方法jarfile
,这被设置为参数的appendToClassPathForInstrumentation
方法。The Java™ Virtual Machine Specification指定后续尝试解析Java虚拟机先前未成功尝试解析的符号引用始终失败,并且由于初始解析尝试而引发的错误相同。 因此,如果JAR文件包含与Java虚拟机未成功尝试解析引用的类相对应的条目,则后续尝试解析该引用将失败,并出现与初始尝试相同的错误。
此方法不会更改
java.class.path
system property
的值。- 参数
-
jarfile
- 系统类加载器未成功搜索类时要搜索的JAR文件。 - 异常
-
UnsupportedOperationException
- 如果系统类加载器不支持附加要搜索的JAR文件。 -
NullPointerException
- 如果jarfile
是null
。 - 从以下版本开始:
- 1.6
- 另请参见:
-
appendToBootstrapClassLoaderSearch(java.util.jar.JarFile)
,ClassLoader.getSystemClassLoader()
,JarFile
-
isNativeMethodPrefixSupported
boolean isNativeMethodPrefixSupported()
返回当前JVM配置是否支持setting a native method prefix 。 设置本机方法前缀的能力是JVM的可选功能。 仅当代理JAR文件中的Can-Set-Native-Method-Prefix
清单属性设置为true
(如package specification中所述)且JVM支持此功能时,才支持设置本机方法前缀。 在单个JVM的单个实例化期间,对此方法的多次调用将始终返回相同的答案。- 结果
- 如果当前JVM配置支持设置本机方法前缀,则返回true,否则返回false。
- 从以下版本开始:
- 1.6
- 另请参见:
-
setNativeMethodPrefix(java.lang.instrument.ClassFileTransformer, java.lang.String)
-
setNativeMethodPrefix
void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix)
此方法通过允许使用应用于名称的前缀进行重试来修改本机方法解析的失败处理。 与ClassFileTransformer
一起使用时,它可以对本机方法进行检测。由于本机方法无法直接检测(它们没有字节码),因此必须使用可以检测的非本机方法进行包装。 例如,如果我们有:
native boolean foo(int x);
我们可以转换类文件(在类的初始定义期间使用ClassFileTransformer),这样就变成了:
boolean foo(int x) { ... record entry to foo ... return wrapped_foo(x); } native boolean wrapped_foo(int x);
其中
foo
成为实际本机方法的包装器,附加前缀为“wrapped_”。 请注意,“wrapped_”将是一个糟糕的前缀选择,因为它可能会形成现有方法的名称,因此像“$$$ MyAgentWrapped $$$ _”这样的东西会更好,但会使这些示例的可读性降低。包装器将允许在本机方法调用上收集数据,但现在问题变得将包装方法与本机实现联系起来。 也就是说,需要将方法
wrapped_foo
解析为foo
的本机实现,其可能是:Java_somePackage_someClass_foo(JNIEnv* env, jint x)
此功能允许指定前缀并进行适当的分辨率。 具体而言,当标准分辨率失败时,考虑前缀重试分辨率。 有两种方法可以解决分辨率,使用JNI功能
RegisterNatives
显式分辨,以及正常的自动分辨率。 对于RegisterNatives
,JVM将尝试此关联:method(foo) -> nativeImplementation(foo)
如果失败,将使用前缀为方法名称的指定前缀重试分辨率,从而产生正确的分辨率:
method(wrapped_foo) -> nativeImplementation(foo)
对于自动解析,JVM将尝试:
method(wrapped_foo) -> nativeImplementation(wrapped_foo)
如果失败,将使用从实现名称中删除的指定前缀重试解决方案,从而产生正确的解决方案:
method(wrapped_foo) -> nativeImplementation(foo)
请注意,由于前缀仅在标准分辨率失败时使用,因此可以选择性地包装本机方法。
由于每个
ClassFileTransformer
都可以对字节码进行自己的转换,因此可以应用多个包装层。 因此每个变压器都需要自己的前缀。 由于转换是按顺序应用的,因此前缀(如果应用)将以相同的顺序应用(请参阅addTransformer
)。 因此,如果三个变换器应用包装器,则foo
可能变为$trans3_$trans2_$trans1_foo
。 但是,如果第二个变压器没有将封装器应用到foo
那么它将只是$trans3_$trans1_foo
。 为了能够有效地确定前缀序列,仅在存在非本地包装器时才应用中间前缀。 因此,在最后一个示例中,即使$trans1_foo
不是本机方法,$trans1_
应用$trans1_
前缀,因为$trans1_foo
存在。- 参数
-
transformer
- 使用此前缀包装的ClassFileTransformer。 -
prefix
- 重试失败的本机方法解析时应用于包装本机方法的前缀。 如果prefix是null
或空字符串,则不会为此转换null
试失败的本机方法解析。 - 异常
-
NullPointerException
- 如果通过null
变压器。 -
UnsupportedOperationException
- 如果JVM的当前配置不允许设置本机方法前缀(isNativeMethodPrefixSupported()
为false)。 -
IllegalArgumentException
- 如果变压器未注册(参见addTransformer
)。 - 从以下版本开始:
- 1.6
-
redefineModule
void redefineModule(模块 module, Set<模块> extraReads, Map<String,Set<模块>> extraExports, Map<String,Set<模块>> extraOpens, Set<类<?>> extraUses, Map<类<?>,List<类<?>>> extraProvides)
重新定义模块以展开它读取的模块集,它导出或打开的软件包集,或它使用或提供的服务。 此方法有助于在命名模块中检测代码,其中该检测需要更改已读取的模块集,导出或打开的包,或使用或提供的服务。此方法不能减少模块读取的模块集,也不能减少它导出或打开的软件包集,也不能减少它使用或提供的服务集。 当调用重新定义未命名的模块时,此方法是无操作。
扩展模块使用或提供的服务时,代理上有责任确保在使用服务类型的每个检测站点都可以访问服务类型。 此方法不检查服务类型是模块的成员,还是检查由其读取的另一个模块导出到模块的包。
extraExports
参数是要导出的其他包的映射。extraOpens
参数是要打开的其他包的映射。 在这两种情况下,映射键是The Java™ Language Specification的 6.5.3节中定义的包的完全限定名称,例如,"java.lang"
。 映射值是应该导出或打开包的非空模块集。extraProvides
参数是模块要提供的附加服务提供程序。 映射键是服务类型。 map值是非空的实现类型列表,每个实现类型都是模块的成员和服务的实现。此方法对于并发使用是安全的,因此允许多个代理在大约同一时间检测和更新同一模块。
- 参数
-
模块
- 重新定义的模块 -
extraReads
- 要读取的可能为空的附加模块集 -
extraExports
- 要导出的其他包的可能空映射 -
extraOpens
- 要打开的其他软件包的可能空图 -
extraUses
- 可能使用的空集附加服务 -
extraProvides
- 可能提供的其他服务的空白地图 - 异常
-
IllegalArgumentException
- 如果extraExports
或extraOpens
包含的密钥不是模块中的软件包; 如果extraExports
或extraOpens
将密钥映射到空集; 如果extraProvides
映射中的值包含不是模块成员或服务实现的服务提供者类型; 或extraProvides
将密钥映射到空列表 -
UnmodifiableModuleException
- 如果模块无法修改 -
NullPointerException
- 如果任何参数为null
或任何集或映射包含null
键或值 - 从以下版本开始:
- 9
- 另请参见:
-
isModifiableModule(Module)
-
isModifiableModule
boolean isModifiableModule(模块 module)
测试是否可以使用redefineModule
修改模块。 如果模块是可修改的,则此方法返回true
。 如果模块不可修改,则此方法返回false
。 当模块是未命名的模块时,此方法始终返回true
(因为重新定义未命名的模块是无操作模块)。- 参数
-
模块
- 用于测试是否可以修改的模块 - 结果
-
true
如果模块可修改,否则false
- 异常
-
NullPointerException
- 如果模块是null
- 从以下版本开始:
- 9
-
-