Hotspot JIT 仅内联小于特定(可配置)大小的方法。因此,使用较小的方法可以实现更多的内联,这很好。
请参阅上的各种内联选项这一页 http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html.
EDIT
详细说明一下:
- 如果一个方法很小,它将被内联,因此几乎不会因为将代码拆分为小方法而受到惩罚。
- 在某些情况下,拆分方法可能会导致更多内联。
Example(如果您尝试的话,完整的代码将具有相同的行号)
package javaapplication27;
public class TestInline {
private int count = 0;
public static void main(String[] args) throws Exception {
TestInline t = new TestInline();
int sum = 0;
for (int i = 0; i < 1000000; i++) {
sum += t.m();
}
System.out.println(sum);
}
public int m() {
int i = count;
if (i % 10 == 0) {
i += 1;
} else if (i % 10 == 1) {
i += 2;
} else if (i % 10 == 2) {
i += 3;
}
i += count;
i *= count;
i++;
return i;
}
}
使用以下 JVM 标志运行此代码时:-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:FreqInlineSize=50 -XX:MaxInlineSize=50 -XX:+PrintInlining
(是的,我使用了证明我的情况的值:m
太大了但是都重构了m
and m2
低于阈值 - 使用其他值您可能会得到不同的输出)。
你会看到m()
and main()
得到编译,但是m()
没有内联:
56 1 javaapplication27.TestInline::m (62 bytes)
57 1 % javaapplication27.TestInline::main @ 12 (53 bytes)
@ 20 javaapplication27.TestInline::m (62 bytes) too big
您还可以检查生成的程序集以确认m
不是内联的(我使用了这些 JVM 标志:-XX:+PrintAssembly -XX:PrintAssemblyOptions=intel
) - 它看起来像这样:
0x0000000002780624: int3 ;*invokevirtual m
; - javaapplication27.TestInline::main@20 (line 10)
如果你像这样重构代码(我已经在单独的方法中提取了 if/else):
public int m() {
int i = count;
i = m2(i);
i += count;
i *= count;
i++;
return i;
}
public int m2(int i) {
if (i % 10 == 0) {
i += 1;
} else if (i % 10 == 1) {
i += 2;
} else if (i % 10 == 2) {
i += 3;
}
return i;
}
您将看到以下编译操作:
60 1 javaapplication27.TestInline::m (30 bytes)
60 2 javaapplication27.TestInline::m2 (40 bytes)
@ 7 javaapplication27.TestInline::m2 (40 bytes) inline (hot)
63 1 % javaapplication27.TestInline::main @ 12 (53 bytes)
@ 20 javaapplication27.TestInline::m (30 bytes) inline (hot)
@ 7 javaapplication27.TestInline::m2 (40 bytes) inline (hot)
So m2
被内联到m
,这是你所期望的,所以我们回到了原来的场景。但当main
被编译后,它实际上内联了整个内容。在程序集级别,这意味着您找不到任何invokevirtual
不再有说明。你会发现这样的行:
0x00000000026d0121: add ecx,edi ;*iinc
; - javaapplication27.TestInline::m2@7 (line 33)
; - javaapplication27.TestInline::m@7 (line 24)
; - javaapplication27.TestInline::main@20 (line 10)
基本上通用指令是“相互化的”。
结论
我并不是说这个例子具有代表性,但它似乎证明了几点:
- 使用更小的方法可以提高代码的可读性
- 较小的方法通常会被内联,因此您很可能不会支付额外方法调用的成本(这将是性能中性的)
- 使用更小的方法might在某些情况下改进全局内联,如上例所示
最后:如果代码的一部分对于这些考虑因素的性能确实至关重要,那么您应该检查 JIT 输出以微调您的代码,并且重要的是在前后进行分析。