现代 JVM 可以不同地优化相同 class 的不同实例吗?

can moderm JVMs optimize different instances of the same class differently?

说我有 2 个相同的 class 实例,但它们的行为不同(遵循不同的代码路径)基于构建时设置的最终布尔字段。所以像:

public class Foo {
   private final boolean flag;

   public Foo(boolean flagValue) {
      this.flag = flagValue;
   }

   public void f() {
      if (flag) {
         doSomething();
      } else {
         doSomethingElse();
      }
   }
}

Foo 的 2 个实例具有不同的 flag 值在理论上可以由 2 个不同的程序集支持,从而消除 if 的成本(对于人为的例子,它是最简单的一个我可以想出)。

所以我的问题是 - 有任何 JVM 真的这样做吗?还是单个 class 总是由单个程序集支持?

以下适用于热点,其他 JVM 可能会应用不同的优化。

如果这些实例依次分配给 static final 字段,然后被其他代码引用并且 VM 以 -XX:+TrustFinalNonStaticFields 启动,那么这些实例可以参与常量折叠和内联 CONSTANT.f() 可以导致不同的分支被淘汰。

特权代码可用的另一种方法是通过 sun.misc.Unsafe.defineAnonymousClass(Class<?>, byte[], Object[]) 创建匿名 classes 而不是实例,并为每个 class 修补一个 class 常量,但最终也是必须通过 class 常量引用才能对优化产生任何影响。

是的,JVM 执行这种形式的优化。在您的情况下,这将是 inlining and adaptive optimization 作为一个始终为真的值的结果。考虑以下代码:

Foo foo = new Foo(true);
foo.f();

为 HotSpot 证明 Foo 始终是 Foof 的调用站点上的实际实例是微不足道的,它允许 VM 简单地复制粘贴代码的方法,从而消除了虚拟分派。内联后,例子缩减为:

Foo foo = new Foo(true);
if (foo.flag) {
  doSomething();
} else {
  doSomethingElse();
}

这又一次允许将代码减少为:

Foo foo = new Foo(true);
foo.doSomething();

是否可以应用优化因此取决于 foo 调用站点的单态性和 flag 在该调用站点的稳定性。 (VM 为此类模式描述您的方法。)VM 能够预测您的程序的结果越少,应用的优化就越少。

如果示例像上面的代码一样简单,JIT 可能还会擦除对象分配并简单地调用 doSomething。此外,对于字段值可以被证明为 true 的简单示例情况,VM 甚至不需要自适应优化,而只是应用上述优化。有一个名为 JITWatch 的很棒的工具,可让您查看代码是如何优化的。