-
public interface Instrumentation
该类提供了用于设计Java编程语言代码所需的服务。 仪器是向方法添加字节码,用于收集工具要使用的数据。 由于这些更改纯粹是加法的,因此这些工具不会修改应用程序的状态或行为。 这种良性工具的示例包括监视代理,剖析器,覆盖分析器和事件记录器。有两种获取
Instrumentation
接口实例的方法:当以指示代理类的方式启动JVM时。 在这种情况下,一个
Instrumentation
实例被传递给代理类的premain
方法。当JVM在JVM启动后的某个时间提供启动代理的机制时。 在这种情况下,一个
Instrumentation
实例被传递给代理代码的agentmain
方法。
这些机制在package specification中有描述。
一旦代理商获取了一个
Instrumentation
实例,代理可以随时调用实例上的方法。- 从以下版本开始:
- 1.5
-
-
方法摘要
所有方法 接口方法 抽象方法 Modifier and Type 方法 描述 void
addTransformer(ClassFileTransformer transformer)
注册提供的变压器。void
addTransformer(ClassFileTransformer transformer, boolean canRetransform)
注册提供的变压器。void
appendToBootstrapClassLoaderSearch(JarFile jarfile)
指定具有由引导类加载器定义的检测类的JAR文件。void
appendToSystemClassLoaderSearch(JarFile jarfile)
指定具有由系统类加载器定义的检测类的JAR文件。Class[]
getAllLoadedClasses()
返回当前由JVM加载的所有类的数组。Class[]
getInitiatedClasses(ClassLoader loader)
返回一个loader
是起始加载程序的所有类的数组。long
getObjectSize(Object objectToSize)
返回指定对象所消耗的存储量的实现特定近似值。boolean
isModifiableClass(Class<?> theClass)
测试课程是否可以修改 retransformation或 redefinition 。boolean
isModifiableModule(Module module)
测试模块是否可以使用redefineModule
进行修改。boolean
isNativeMethodPrefixSupported()
返回当前的JVM配置是否支持 setting a native method prefix 。boolean
isRedefineClassesSupported()
返回当前的JVM配置是否支持重新定义类。boolean
isRetransformClassesSupported()
返回当前JVM配置是否支持对类的重新创建。void
redefineClasses(ClassDefinition... definitions)
使用提供的类文件重新定义提供的一组类。void
redefineModule(Module module, Set<Module> extraReads, Map<String,Set<Module>> extraExports, Map<String,Set<Module>> extraOpens, Set<Class<?>> extraUses, Map<Class<?>,List<Class<?>>> extraProvides)
重新定义一个模块来扩展它读取的模块集,它导出或打开的一组包,或者它使用或提供的服务。boolean
removeTransformer(ClassFileTransformer transformer)
注销提供的变压器。void
retransformClasses(Class<?>... 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
为真,并且JVM的当前配置不允许重新转换(isRetransformClassesSupported()
为假) - 从以下版本开始:
- 1.6
-
addTransformer
void addTransformer(ClassFileTransformer transformer)
注册提供的变压器。与
addTransformer(transformer, false)
相同。- 参数
-
transformer
- 要注册的变压器 - 异常
-
NullPointerException
- 如果通过了一个null
变压器 - 另请参见:
-
addTransformer(ClassFileTransformer,boolean)
-
removeTransformer
boolean removeTransformer(ClassFileTransformer transformer)
注销提供的变压器。 未来类定义将不会显示给变压器。 删除最新添加的变压器匹配实例。 由于类加载的多线程特性,变压器可以在其被移除之后接收呼叫。 应该防范变形金刚来预期这种情况。- 参数
-
transformer
- 变压器取消注册 - 结果
- 如果发现和移除变压器,则为真,如果没有找到变压器,则为真
- 异常
-
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(Class<?>... classes) throws UnmodifiableClassException
重新转换提供的一组类。此功能有助于已加载类的检测。 当类最初加载或者当它们是redefined时 ,初始类文件字节可以用
ClassFileTransformer
进行转换。 此函数重新运行转换过程(无论以前是否发生转换)。 此转发遵循以下步骤:- 从初始类文件字节开始
- 对于添加了
canRetransform
每个变压器,在最后一个类加载或重定义期间由transform
返回的字节被重新用作转换的输出; 请注意,这相当于重新应用以前的转换,未改变; 除了不调用transform
方法。 - 对于添加了
canRetransform
每个变压器,transform
方法在这些变压器中被调用 - 转换的类文件字节作为类的新定义安装
转换顺序在
ClassFileTransformer
中有所描述。 在重新编码无法转换的自动重新应用中使用相同的顺序。初始类文件字节表示传递给
ClassLoader.defineClass
或redefineClasses
(在应用任何转换之前)的字节,但它们可能不完全匹配它们。 常量池可能不具有相同的布局或内容。 常量池可能有更多或更少的条目。 常量池条目可能有不同的顺序; 然而,方法的字节码中的常量池索引将对应。 某些属性可能不存在。 如果订单无意义,例如方法的顺序,订单可能不会被保留。该方法对一组进行操作,以允许同时对多个类进行相互依赖的更改(类A的重新转换可能需要对类B进行重新转换)。
如果重新转换的方法具有活动堆栈帧,则这些活动帧将继续运行原始方法的字节码。 重新构建的方法将用于新的调用。
该方法不会导致任何初始化,除了在常规JVM语义下会发生。 换句话说,重新定义一个类并不会导致它的初始化器被运行。 静态变量的值将保持在调用之前。
转载类的实例不受影响。
重新转换可能会改变方法体,常量池和属性。 重新传输不能添加,删除或重命名字段或方法,更改方法的签名或更改继承。 这些限制可能在将来的版本中解除。 类文件字节不会被检查,验证和安装,直到应用转换为止,如果结果字节错误,则此方法将抛出异常。
如果此方法抛出异常,则不会重新创建任何类。
该方法用于仪器仪表,如class specification所述 。
- 参数
-
classes
- 要classes
的类的数组; 允许零长度数组,在这种情况下,此方法什么也不做 - 异常
-
UnmodifiableClassException
- 如果指定的类不能被修改(isModifiableClass(java.lang.Class<?>)
将返回false
) -
UnsupportedOperationException
- 如果JVM的当前配置不允许重新传输(isRetransformClassesSupported()
为假)或重新尝试进行不受支持的更改 -
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语义下会发生。 换句话说,重新定义一个类并不会导致它的初始化器被运行。 静态变量的值将保持在调用之前。
重新定义的类的实例不受影响。
重新定义可能会改变方法体,常量池和属性。 重定义不能添加,删除或重命名字段或方法,更改方法的签名或更改继承。 这些限制可能在将来的版本中解除。 类文件字节不会被检查,验证和安装,直到应用转换为止,如果结果字节错误,则此方法将抛出异常。
如果此方法抛出异常,则不会重新定义任何类。
该方法用于仪器仪表,如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(Class<?> 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
Class[] getAllLoadedClasses()
返回当前由JVM加载的所有类的数组。- 结果
- 一个包含JVM加载的所有类的数组,如果没有,则为零长度
-
getInitiatedClasses
Class[] 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_foo
存在。- 参数
-
transformer
- 使用此前缀包装的ClassFileTransformer。 -
prefix
- 重试本机方法解析失败时应用于包装本地方法的前缀。 如果前缀为null
或空字符串,则不会为此变压器重试本机方法分辨率失败。 - 异常
-
NullPointerException
- 如果通过了一个null
变压器。 -
UnsupportedOperationException
- 如果JVM的当前配置不允许设置本机方法前缀(isNativeMethodPrefixSupported()
为false)。 -
IllegalArgumentException
- 如果变压器未注册(见addTransformer
)。 - 从以下版本开始:
- 1.6
-
redefineModule
void redefineModule(Module module, Set<Module> extraReads, Map<String,Set<Module>> extraExports, Map<String,Set<Module>> extraOpens, Set<Class<?>> extraUses, Map<Class<?>,List<Class<?>>> extraProvides)
重新定义一个模块来扩展它读取的模块集,它导出或打开的一组包,或者它使用或提供的服务。 该方法便于在命名模块中对代码进行检测,其中该工具需要更改读取的模块集,导出或打开的软件包,或使用或提供的服务。此方法无法减少模块读取的模块集,也不能减少其导出或打开的软件包集,也不能减少其使用或提供的一组服务。 这个方法是被调用来重新定义一个未命名的模块的无操作。
当扩展模块所使用或提供的服务时,该代理可以在代理上确保在使用服务类型的每个工具站点都可访问该服务类型。 该方法不检查服务类型是否是模块的成员,或者是由其读取的另一个模块导出到该模块的包中。
extraExports
参数是要导出的其他包的映射。extraOpens
参数是要打开的附加包的地图。 在这两种情况下,映射密钥是The Java™ Language Specification第6.5.3节中定义的软件包的全限定名称,例如"java.lang"
。 映射值是包应该导出或打开的非空集合模块。extraProvides
参数是模块提供的附加服务提供商。 地图键是服务类型。 映射值是实现类型的非空列表,每个实现类型都是模块的成员和服务的实现。该方法对于并发使用是安全的,因此允许多个代理在同一时间进行仪器和更新同一模块。
- 参数
-
module
- 模块重新定义 -
extraReads
- 要读取的可能空的一组附加模块 -
extraExports
- 要导出的附加包的可能空的地图 -
extraOpens
- 要打开的附加包的可能空的地图 -
extraUses
- 可能是空的一组额外的服务使用 -
extraProvides
- 可能提供的附加服务的空地图 - 异常
-
IllegalArgumentException
- 如果extraExports
或extraOpens
包含不是模块中的包的密钥; 如果extraExports
或extraOpens
将一个键映射到一个空集合; 如果extraProvides
映射中的值包含不是模块成员或服务实现的服务提供者类型; 或extraProvides
将键映射到空列表 -
UnmodifiableModuleException
- 如果模块无法修改 -
NullPointerException
- 如果任何参数是null
或任何集合或地图包含null
键或值 - 从以下版本开始:
- 9
- 另请参见:
-
isModifiableModule(Module)
-
isModifiableModule
boolean isModifiableModule(Module module)
测试模块是否可以用redefineModule
进行修改。 如果模块是可修改的,那么此方法将返回true
。 如果模块不可修改,则此方法返回false
。 当模块是未命名模块时,该方法总是返回true
(因为重新定义未命名的模块是无操作的)。- 参数
-
module
- 模块测试是否可以修改 - 结果
-
true
如果模块是可修改的,否则为false
- 异常
-
NullPointerException
- 如果模块是null
- 从以下版本开始:
- 9
-
-