3 代码实战验证
3.1 全无优化的代码
public int test(int x) { int xx = x + 2; Point p = new Point(xx, 42); return p.getX();
3.2 优化step1:内联构造器和getX()方法
public int test(int x) { int xx = x + 2; // 在堆中分配P对象 Point p = point_memory_alloc(); // Point构造器被内联后 p.x = xx; p.y = 42; // Point::getX()被内联后 return p.x;
优化step2:标量替换
逃逸分析后,发现在整个test()方法的范围内Point对象实例不会发生任何程度逃逸, 便可对它进行标量替换:把其内部的x和y直接置换出来,分解为test()方法内的局部变量,从而避免了Point对象实例的创建
public int test(int x) { int xx = x + 2; int px = xx; int py = 42 return px; }
step3:无效代码消除
数据流分析,发现py的值其实对方法不会造成任何影响,那就可以放心地去做无效代码消除得到最终优化结果,如下所示:
public int test(int x) { return x + 2;
观察测试结果,实施逃逸分析后的程序在MicroBenchmarks中往往能得到不错的成绩,但在实际应用程序中,尤其是大型程序中反而发现实施逃逸分析可能出现效果不稳定,或分析过程耗时但却无法有效判别出非逃逸对象而导致性能(即时编译的收益)下降,所以曾经在很长的一段时间,即使是服务端编译器,也默认不开启逃逸分析(从JDK 6 Update 23开始,服务端编译器中开始才默认开启逃逸分析。),甚至在某些版本(如JDK 6 Update 18)中还曾完全禁止这项优化,一直到JDK 7时这项优化才成为服务端编译器默认开启的选项。
若有需要或确认对程序有益,可使用参数:
- -XX:+DoEscapeAnalysis 手动开启逃逸分析
开启后可通过参数:
-XX:+PrintEscapeAnalysis 查看分析结果
有逃逸分析支持后,用户可使用如下参数:
-XX:+EliminateAllocations 开启标量替换
+XX:+EliminateLocks 开启同步消除
-XX:+PrintEliminateAllocations 查看标量的替换情况
让我们一起期待该JIT优化技术之逃逸分析的发展。
参考
《深入理解 Java 虚拟机》