逃逸分析是指分析一个变量的作用域,看这个变量会不会逃逸到方法外,如果不会的话,则可以对这个变量进行一些优化。《深入理解java虚拟机》中是这样说的:
逃逸分析的基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,他可能被外部方法所引用,例如作为调用参数传递到其他的方法中,称为方法逃逸。甚至还有可能被外部线程访问到,譬如复制非类变量或可以在其他线程中访问的实例变量,称为线程逃逸。
如果能证明一个对象不会逃逸到方法或线程之外,也就是别的方法或线程无法通过任何途径访问到这个对象,则可能为这个变量进行一些高效的优化。

这里的优化有:栈上分配、同步消除、标量替换。

由于我看的是第二版的书,当时还是jdk1.7,书上说1.7上的hotspot没有实现栈上分配,我好奇现在常用的1.8有没有实现,所以做了个小实验:

public static void main(String[] args) {
        Main m =new Main();
        long startTime=System.currentTimeMillis();
        if(true){
            while (true) {
                m.fun();
            }
        }
        long endTime=System.currentTimeMillis();
        System.out.println(endTime-startTime);
    }
    void fun(){
        Object o = new Object();
    }

上边的代码就是不断循环fun函数,由于fun函数中的变量 o 没有逃逸出方法,如果jdk1.8实现了栈上分配的话,堆空间的内存占用就不会一直增加,自然也不需要gc。实际结果如下图:

java代码逃逸 java变量逃逸_java代码逃逸

可以看出,程序内存占用是在不断上升的,而且也触发了GC,说明jdk1.8也没有实现栈上分配。

那么,标量替换能给效率带来多大的提升呢?

public static void main(String[] args) {
        Main m =new Main();
        long startTime=System.currentTimeMillis();
        for(int i=0;i<1000000;i++) {
            m.fun();
        }
        long endTime=System.currentTimeMillis();
        System.out.println(endTime-startTime);
    }
    void fun(){
        Person p = new Person();
    }
    class Person{
        int a;
        int b;
        int c;
        int d;
    }

这段代码反复执行fun函数100万次,我们首先开启逃逸分析和标量替换,结果如下:

5

一共运行了5毫秒。
然后关闭标量替换,结果如下:

[GC (Allocation Failure) [PSYoungGen: 8192K->840K(9216K)] 8192K->848K(19456K), 0.0009078 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 9032K->840K(9216K)] 9040K->848K(19456K), 0.0006650 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 9032K->824K(9216K)] 9040K->832K(19456K), 0.0006836 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 9016K->760K(9216K)] 9024K->768K(19456K), 0.0005237 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
10

程序执行了4次minorGC,这也导致执行时间延长了一倍。

总结:jdk1.8目前也没有实现栈上分配对象;标量替换能节省大量的内存空间。这给我们提了个醒,我们在写代码时,如果不是必要,尽量让变量不逃逸出方法。