JavaAgent中redefine和retransform的区别

15

当使用自定义Java代理程序打包JAR文件时,您可以添加以下属性:

  • Can-Redefine-Classes:允许重新定义类。
  • Can-Retransform-Classes:允许重转换类。

这两者之间有什么区别?

如果重新定义发生在类加载之前,而重转换发生在之后,那么重转换是何时发生的?

3个回答

18
They seem almost redundant in the functionality they offer us. The main difference seems to be that when we redefine a class, we supply a byte[] with the new definition out of the blue, whereas when we retransform, we get a byte[] containing the current definition via the same API, and we return a modified byte[].
Therefore, to redefine, we need to know more about the class. Consider the use-case of injecting profiling trace statements. With retransform you can do that more directly: just look at the bytecode given, modify it, and return it. But if we went the redefine route, we would need to fetch the original byte[] from somewhere (like getResourceAsStream()).
Another apparent difference is in how we interact with other class transformers; who goes first. Transforms are applied to the original or a redefined class, so several transforms can be additive, for example.

从历史上看,如果我们查看API文档中的Since注释,或者在这本书(Friesen 2007 Beginning Java SE 6 Platform)的第238页上,我们会注意到重定义功能是在Java 5中引入的,而重转换则是在Java 6中引入的。我猜重转换被引入作为一种更通用的能力,但为了向后兼容性,必须保留重定义

引用上述链接书籍中关于“重新转换”方法的关键句子:
代理使用这些方法重新转换之前加载的类,而无需访问它们的类文件。
问题的第二部分:
如果重定义发生在类加载之前,而重转换发生在类加载之后,那么什么时候确切地发生了重转换?
不,重定义和重转换都发生在类加载之后。它们发生在您调用Instrumentation实例的redefineClasses(..)retransformClasses(..)方法时,分别执行重定义和重转换操作。
这里有一个问题,问路过的任何专家:通过重新定义类,是否有任何不能通过重新转换类来做到的事情?我猜答案是“没有”。

8
重新定义意味着代理程序将在任意时间点调用Instrumentation.redefineClasses以更改现有(已加载)类的实际定义。代理会提供新定义的字节码。
重新转换是指在类加载时通常应用的类文件转换过程。代理可以注册ClassFileTransformers,这些转换器依次被调用以在类初始化之前对字节码应用转换。因此,重新转换指的是JVM重复这个过程以针对已加载的类。在这种情况下,代理可能会调用Instrumentation.retransformClasses来指定要重新转换哪些类,但不会提供字节码。相反,JVM将调用所有已注册的可重新转换的ClassFileTransformers,提供实际的字节码(或链式转换器的前一个转换器的结果)。

-1

重新转换:

转换是指在类加载时通常应用的类文件转换过程。代理可以注册ClassFileTransformers,这些转换器会依次调用以在类初始化之前对字节码应用转换。因此,重新转换是JVM重复对已加载类执行此过程的能力。

重新转换与为已加载类进行类/方法检测具有相同的功能。

重新转换存在的问题: JVM将字节码存储在PermGen(Java 7或更低版本)或Metaspace(Java 8或更高版本)中。每次代理重新转换类/方法时,JVM都会在PermGen或Metaspace中保存修改后的字节码副本。过度的重新转换可能导致内存泄漏。

IBM Java在重新转换方面有额外的10% CPU开销。这就是为什么在IBM Java代理中禁用重新转换的原因。 大约80%的JVM崩溃是由于重新转换-这并不意味着JVM大部分时间都会崩溃,但每当我们遇到JVM崩溃问题时,大约80%的时间是由于重新转换造成的。

禁用重新转换: 有一个Java选项系统属性(-Dappdynamics.agent.disable.retransformation=true)可用于禁用重新转换。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接