Java: 什么类 不能通过代理或MBean 进行转换?

Java: What classes can not be transformed by an agent or by MBean?

我想知道我无法使用字节码 t运行sformation 和 java 代理拦截和操纵哪些 classes。

Q1:我知道并非所有 classes 都可以在加载时和以后重新定义(更改、操作)。这些 classes 包括非本机方法,但被硬连线的本机实现所取代(如某些 Spring 和系统方法)。我想知道哪些 class 是字节码操作的限制,其中一个或两个加载 and/or 加载后重新定义?

问题 2:对于最近的 JDK/JRE 版本,这组无法更改的类型和方法是否发生了变化?

问题 3:如果我通过更改默认的 class 加载器来操纵 JVM(你没有花哨的更改),我是否能够增加可能重新定义的类型的数量以及什么和为什么?

[附加]

我做了很多研究,甚至 运行 我自己也做了一个代理人,看看会发生什么。基本上有相当多的 classes 缺失。在 class 路径中加载所有 JRE classes,在代理机制启动之前会看到一些缺失的 classes 已加载。这通常是有原因的,因为即使是代理也需要 classes 到 运行 而那些 classes 需要 classes...。但我想知道一组 class 永远无法改变的东西是什么,为什么如此以及破解 JVM 能否让你更进一步。

我尝试涉足 JVM / Java 操作,基本上是为了理解所有内容,并在我的工具箱中添加一些不错的监控工具。此外,我正在为我的更大项目实施 class 重新加载解决方案。

嗯,我用

做了一个简单的测试
public static void premain(String agentArgs, Instrumentation inst) {
  System.out.println(System.getProperty("java.version"));
  System.out.println(inst.isRedefineClassesSupported());
  int num=0;
  for(Class<?> clazz:inst.getAllLoadedClasses())
    if(!clazz.isArray() && !inst.isModifiableClass(clazz)) {
      System.out.println("not modifiable "+clazz);
      num++;
    }
  System.out.println((num==0? "all classes are": num+" classes are not")+" transformable");
}

1.7.0_511.8.0_60 开始,它打印:

true
all classes are transformable

也就是说,你的第一个假设是错误的。对于以后可以重新定义哪些 classes 没有限制。当然,当你重新定义必要的 classes 时,你必须非常小心,以免搞砸一切。

当您假设 java 代理启动时,您是对的,已经加载了 class 无法进行加载时转换的元素。请注意,您可以使用上面代码中使用的相同方法 getAllLoadedClasses() 来了解哪些 class 已经加载。在我的设置中,它 returns 超过 400 个非数组 classes.

您可以对它们做出的唯一安全假设是,确切的集合是故意未指定的,否则,不可能更改 JVM bootstrap 进程的实现。这将是一个严格的限制——由非标准功能强加……


操纵系统 class 加载器不会改变任何东西,因为这些 classes 不是由系统 class 加载器加载,而是由 bootstrap装载机。这是 class 加载器,表示为 null,因为它不能由 Java 对象实例表示。

您可以通过以下方式验证:

public static void premain(String agentArgs, Instrumentation inst) {
  System.out.println(System.getProperty("java.version"));
  System.out.println(inst.isRedefineClassesSupported());
  int num=0;
  for(Class<?> clazz:inst.getAllLoadedClasses())
    if(clazz.getClassLoader()!=null) {
      System.out.println("already loaded "+clazz);
      num++;
    }
  System.out.println(num+" non-bootstrap class(es) loaded");
}

在我的设置中,它打印了:

1.8.0_60
true
already loaded class ExperimentalAgent
1 non-bootstrap class(es) loaded

显示通过系统 class 加载器加载的唯一 class 是代理本身,所有其他已经存在的 classes 都由 bootstrap 加载和定义装载机。

当系统 class 加载器加载的 classes 包含对核心 classes 的引用时,它们通过询问系统 class 加载器来解决,但是,它除了委托给它的父加载器以兼容的方式解决它们之外别无选择。否则,重新定义的 classes 被认为与 bootstrap 加载程序中解析的 classes 不同(即当核心 classes 相互引用时)。请注意,对于限定名称以 java. 开头的 classes,尝试在 Java 端 class 加载程序定义它们无论如何都会被拒绝:

ClassLoader.defineClass:

Throws:

SecurityException - If an attempt is made to add this class to a package that contains classes that were signed by a different set of certificates than this class (which is unsigned), or if name begins with "java.".