JVM优化之逃逸分析

来源:互联网 发布:马克思资本论 知乎 编辑:程序博客网 时间:2024/05/22 10:59

转载自: http://kenwublog.com/jvm-optimization-escape-analysis

在编程语言的编译优化原理中,分析指针动态范围的方法称之为逃逸分析。

通俗一点讲,就是当一个对象的指针被多个方法或线程引用时,我们称这个指针发生了逃逸。
而用来分析这种逃逸现象的方法,就称之为逃逸分析。
举个例子:
Java代码

  1. class A {
  2.     public static B b;  

  3.     public void globalVariablePointerEscape() { // 给全局变量赋值,发生逃逸
  4.         b = new B();
  5.     }  

  6.     public B methodPointerEscape() { // 方法返回值,发生逃逸
  7.         return new B();
  8.     }  

  9.     public void instancePassPointerEscape() {
  10.         methodPointerEscape().printClassName(this); // 实例引用传递,发生逃逸
  11.     }
  12. }  

  13. class B {
  14.     public void printClassName(A a) {
  15.         System.out.println(a.class.getName());
  16.     }
复制代码


在这个例子中,一共举了3种常见的指针逃逸场景。分别是 全局变量赋值,方法返回值,实例引用传递。
逃逸分析优化JVM原理我们知道java对象是在堆里分配的,在调用栈中,只保存了对象的指针。
当对象不再使用后,需要依靠GC来遍历引用树并回收内存,如果对象数量较多,将给GC带来较大压力,也间接影响了应用的性能。减少临时对象在堆内分配的数量,无疑是最有效的优化方法。
接下来,举一个场景来阐述。
假设在方法体内,声明了一个局部变量,且该变量在方法执行生命周期内未发生逃逸(在方法体内,未将引用暴露给外面)。
按照JVM内存分配机制,首先会在堆里创建变量类的实例,然后将返回的对象指针压入调用栈,继续执行。
这是优化前,JVM的处理方式。
逃逸分析优化 – 栈上分配
优化原理:分析找到未逃逸的变量,将变量类的实例化内存直接在栈里分配(无需进入堆),分配完成后,继续在调用栈内执行,最后线程结束,栈空间被回收,局部变量也被回收。
这是优化后的处理方式,对比可以看出,主要区别在栈空间直接作为临时对象的存储介质。从而减少了临时对象在堆内的分配数量。
逃逸分析另一个重要的优化 – 锁省略
如果通过逃逸分析能够判断出指向某个局部变量的多个引用被限制在同一方法体内,并且所有这些引用都不能“逃逸”到这个方法体以外的地方,那么HotSpot会要求JIT执行一项优化动作 – 将局部变量上拥有的锁省略掉。
这就是锁省略(lock elision)。
性能测试

  1. class DoubleSlot {
  2.     final int value1;
  3.     final int value2;

  4.     public DoubleSlot(int value1, int value2) {
  5.       this.value1 = value1;
  6.       this.value2 = value2;
  7.     }
  8.   }

  9.   static int slotValue(DoubleSlot slot) {
  10.     return slot.value1 + slot.value2;
  11.   }

  12.   static int sum(int[] values) {
  13.     int sum = 0;
  14.     int length = values.length;
  15.     for(int i=1; i<100; i++)
  16.       sum(values);

  17.     for(int i=0; i<100; i++)
  18.       test(values);
  19.   }
复制代码
测试结果是:
$ /usr/jdk/jdk1.6.0_14/bin/java -server EscapeAnalysisTest
time 8889261
$ /usr/jdk/jdk1.6.0_14/bin/java -server -XX:+DoEscapeAnalysis EscapeAnalysisTest
time 1408140
从结果中,可以看到,启用逃逸分析的运行性能6倍于未启用。
JVM中启用逃逸分析 DoEscapeAnalysis安装jdk1.6.0_14,运行java时传递jvm参数  -XX:+DoEscapeAnalysis
参考资料:
http://www.cag.csail.mit.edu/~rinard/pointer_and_escape_analysis/
http://developer.amd.com/documen ... s/01302008_jvm.aspx
http://en.wikipedia.org/wiki/Escape_analysis
http://en.wikipedia.org/wiki/Jav ... and_lock_coarsening
http://staff.ustc.edu.cn/~yuzhang/papers/cncc07.pdf (一种实现方法,过于学术,不过引言部分写得很不错)
http://java.dzone.com/articles/escape-analysis-java-6-update
http://en.wikipedia.org/wiki/Non-negative_matrix_factorization
0 0