可见性和javac / JVM内联

时间:2014-10-01 15:23:35

标签: java jvm javac jit inlining

方法/字段可见性如何影响Java中的方法内联?

我想到的情况类似于public字段的private getter:

private Thing blah;

public Thing getBlah() {
    return blah;
}

这里出现了几个问题。

首先,Java编译器本身是否进行任何内联?这个问题似乎得到了不同的回答,有些人说yes,有些人说no。我不知道是不是因为它过去没有进行任何内联,但现在确实如此,或者只是某些人是对的而有些人是错的......

现在,如果它确实做了一些内联,我是否正确地认为它不可能内联getBlah()个调用?它们是内联显而易见的显而易见的地方,因为该方法非常简单,并且调用方法的开销与方法本身的代码一样大。但如果它被编译器内联,你最终会得到直接访问private字段的字节码;肯定JVM会抱怨吗? (即使此方法为static final,这也适用。)

其次,JIT编译器怎么样?据我所知,当涉及到该级别的内联时,此问题不适用。一旦它生成本机代码,JVM就已经完成了检查,并确认我可以调用该方法(因为它是public);所以它可以生成内联代码,内联调用,没有任何可见性问题......是吗?

3 个答案:

答案 0 :(得分:1)

  1. javac 内联方法,即使只是getBlah()setBlah()
  2. 这么简单
  3. 对于HotSpot JVM,JIT编译器 内联所有此类方法,除非它达到最大内联级别(-XX:MaxInlineLevel
  4. JIT在内联方面同样对待publicprivate方法。除非某些very specific cases
  5. ,否则访问修饰符通常不会影响内联

答案 1 :(得分:1)

javac编译器(以及任何有效的java编译器)不会也不能内联getter;想一想:你可以从该类扩展一个类并覆盖getter。是的,如果编译器会过分强调内联访问它不会通过验证程序(至少它应该不通过验证程序,但它们不会验证所有内容 - 在java 1.3中你甚至可以使main()私有,它仍然可以工作...同样在javac中有一个-O选项 有时会搞砸你的代码。)

JIT是另一头野兽;如果某种方法存在重叠,它就知道(至少现在是这样)。即使稍后加载了一个覆盖getter的类,它也可以去优化已经JIT的方法来重新选择inhertance树上的改动(这是AOT编译器缺少其中一个优化信息)。

因此它可以安全地内联它想要的任何东西。它也不需要人工维护访问修改器,因为在编译的机器代码中没有这样的东西,并且它再次知道什么是vaild代码转换(并且因为getter是如此常见,它也是一个低悬的果实JIT优化)。

编辑:为了使其绝对清楚,上面的段落涉及潜在的虚拟方法;特别是那些非私人,静态或最终的。在某些情况下,Javac可以执行内联;因为它可以证明那些不存在任何覆盖。面对JIT也这样做的事实,这将是一个毫无意义的事情,并且它在这方面做得更好。

答案 2 :(得分:0)

任何特定的Java编译器(例如Oracle)是否执行任何内联都是一个实现细节,您最好忽略它。您选择的编译器或替代编译器的下一代可能与您现在正在查看的编译器的运行方式不同。

但是,就Java编译器执行内联而言,它只能内联private个方法和构造函数。其他任何东西都是从(不可知的)其他类访问的候选者。

另一方面,JIT编译器可以做任何想做的事情,包括内联公共方法。它负责加载新类时可能需要的任何调整。 Beans风格的访问器方法是JIT优化的一种特别可能的方法,它们是如此常见的模式。