说我有2个相同类的实例,但它们的行为不同(遵循不同的代码路径)基于构造时设置的最终布尔字段。所以像:
public class Foo {
private final boolean flag;
public Foo(boolean flagValue) {
this.flag = flagValue;
}
public void f() {
if (flag) {
doSomething();
} else {
doSomethingElse();
}
}
}
Foo
flag
的两个{{1}}个实例理论上可以由两个不同的程序集支持,从而消除了if的成本(对于人为的例子,对不起,这是我能做的最简单的例子)想出来。)
所以我的问题是 - 任何JVM实际上都这样做了吗?或者是一个总是由单个程序集支持的单个类?
答案 0 :(得分:7)
是的,JVM采用这种形式的优化。在您的情况下,这将是inlining and adaptive optimization的结果,因为它是一个永远为真的值。请考虑以下代码:
Foo foo = new Foo(true);
foo.f();
为HotSpot证明Foo
始终是Foo
的调用网站上的f
的实际实例,这是微不足道的,因为它允许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的强大工具,可以让您了解代码的优化方式。
答案 1 :(得分:5)
以下内容适用于热点,其他JVM可能会应用不同的优化。
如果这些实例已转为分配给static final
字段,然后由其他代码引用,并且VM以-XX:+TrustFinalNonStaticFields
启动,那么这些实例可以参与常量折叠和内联CONSTANT.f()
可以导致不同的分支被淘汰。
特权代码可用的另一种方法是通过sun.misc.Unsafe.defineAnonymousClass(Class<?>, byte[], Object[])
创建匿名类而不是实例,并为每个类修补类常量,但最终还必须通过类常量引用以对优化产生任何影响。