JVM中的逃逸分析

逃逸分析(Escape Analysis)是目前Java虚拟机中比较前沿的优化技术。 java

逃逸分析的基本行为就是分析对象动态做用域:当一个对象在方法中被定义后,它可能被外部方法所引用,例如做为调用参数传递到其余地方中,称为方法逃逸。 算法

例如: 并发

public static StringBuffer craeteStringBuffer(String s1, String s2) {
        StringBuffer sb = new StringBuffer();
        sb.append(s1);
        sb.append(s2);
        return sb;
    }
StringBuffer sb是一个方法内部变量,上述代码中直接将sb返回,这样这个StringBuffer有可能被其余方法所改变,这样它的做用域就不仅是在方法内部,虽然它是一个局部变量,称其逃逸到了方法外部。

甚至还有可能被外部线程访问到,譬如赋值给类变量或能够在其余线程中访问的实例变量,称为线程逃逸。 app

上述代码若是想要StringBuffer sb不逃出方法,能够这样写: 高并发

public static String createStringBuffer(String s1, String s2) {
        StringBuffer sb = new StringBuffer();
        sb.append(s1);
        sb.append(s2);
        return sb.toString();
    }
不直接返回 StringBuffer,那么StringBuffer将不会逃逸出方法。

若是能证实一个对象不会逃逸到方法或线程外,则可能为这个变量进行一些高效的优化。
性能

1. 栈上分配

咱们都知道Java中的对象都是在堆上分配的,而垃圾回收机制会回收堆中再也不使用的对象,可是筛选可回收对象,回收对象还有整理内存都须要消耗时间。若是可以经过逃逸分析肯定某些对象不会逃出方法以外,那就可让这个对象在栈上分配内存,这样该对象所占用的内存空间就能够随栈帧出栈而销毁,就减轻了垃圾回收的压力。 优化

在通常应用中,若是不会逃逸的局部对象所占的比例很大,若是能使用栈上分配,那大量的对象就会随着方法的结束而自动销毁了。 spa

2. 同步消除

这一块之前提到过,请参考[高并发Java 九] 锁的优化和注意事项中 锁消除 章节 .net

3. 标量替换

Java虚拟机中的原始数据类型(int,long等数值类型以及reference类型等)都不能再进一步分解,它们就能够称为标量。相对的,若是一个数据能够继续分解,那它称为聚合量,Java中最典型的聚合量是对象。若是逃逸分析证实一个对象不会被外部访问,而且这个对象是可分解的,那程序真正执行的时候将可能不建立这个对象,而改成直接建立它的若干个被这个方法使用到的成员变量来代替。拆散后的变量即可以被单独分析与优化,能够各自分别在栈帧或寄存器上分配空间,本来的对象就无需总体分配空间了。 线程

4. 总结

虽然概念上的JVM老是在Java堆上为对象分配空间,但并非说彻底依照概念的描述去实现;只要最后实现处理的“可见效果”与概念中描述的一直就没问题了。因此说,“you can cheat as long as you don't get caught”。Java对象在实际的JVM实现中可能在GC堆上分配空间也可能在栈上分配空间也可能彻底就消失了。这种行为从Java源码中看不出来,也没法显式指定,只是聪明的JVM自动作的优化而已。

可是逃逸分析会有时间消耗,因此性能未必提高多少,而且因为逃逸分析比较耗时,目前的实现都是采用不那么准确可是时间压力相对较小的算法来完成逃逸分析,这就可能致使效果不稳定,要慎用

因为HotSpot虚拟机目前的实现方法致使栈上分配实现起来比较复杂,由于在HotSpot中暂时尚未作这项优化。

Reference:

1. http://rednaxelafx.iteye.com/blog/659108/
相关文章
相关标签/搜索