1

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

我想到的情况类似于字段的public吸气剂private

private Thing blah;

public Thing getBlah() {
    return blah;
}

这里出现了几个问题。

一方面,Java 编译器本身是否进行内联?这个问题似乎有不同的回答,有人说,有人说不是。我不知道那是因为它以前不做任何内联,但现在做了,还是只是有些人是对的,有些人是错的......

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

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

4

3 回答 3

1
  1. javac 内联方法,甚至像getBlah()setBlah()
  2. 对于 HotSpot JVM,JIT 编译器内联所有此类方法,除非它达到内联的最高级别 ( -XX:MaxInlineLevel)
  3. JIT 在内联方面同等对待public和处理private方法。除非某些非常特殊的情况,否则访问修饰符通常不会影响内联。
于 2014-10-01T15:51:50.013 回答
1

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

JIT 完全是另外一头野兽。它在任何时候都知道(至少现在)是否有一个方法的覆盖。即使稍后加载了覆盖 getter 的类,它也可以取消优化已经 JIT 的方法以反映继承树上的更改(这是 AOT 编译器缺少信息的优化之一)。

因此它可以安全地内联它想要的任何东西。它也不需要人为地维护访问修改器,因为在编译的机器代码中没有这样的东西,并且它再次知道什么是有效的代码转换(并且由于 getter 如此普遍,它对于 JIT 来说也是一个唾手可得的果实优化)。

编辑:为了明确起见,以上段落涉及潜在的虚拟方法;特别是那些不是私有的、静态的或最终的。Javac 在某些情况下可以执行内联;因为它可以证明那些不存在覆盖。面对 JIT 也这样做的事实,这将是一项毫无意义的工作,而且它在这方面做得更好。

于 2014-10-01T17:51:00.933 回答
0

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

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

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

于 2014-10-01T15:40:19.490 回答