Java虚拟机的逃逸分析

一个对象里的静态成员变量和静态方法是存储在方法区中的,伴随着类而存在的。堆和方法区是线程共享的,线程安全问题通常不会发生在方法区,而多发生于堆中。线程一旦启动,便会在虚拟机栈中开辟一个属于自己且独立于其它线程的栈区域。线程每调用一个方法(无论是静态方法还是实例方法),其栈区域中就会生成一个栈帧,该栈帧用来存储被调用方法里所有的局部变量(如果未开启逃逸分析或在开启逃逸分析的情况该对象逃逸,则在方法中创建的对象会保存在堆中。当然可以肯定的是,方法里创建的对象引用一定是存储在栈帧里的)。线程执行方法的时候对应内存的进栈操作,执行完毕后对应内存的出栈操作,一旦出栈就会释放内存,回收方法里的局部变量。但方法里创建的对象不会因为出栈而立马在堆中销毁,它会等待Java的垃圾回收算法判断其是否还有引用指向它,若无引用指向它,便会回收该对象。由此可见,Java的垃圾回收机制只在堆和方法区中起作用,并不会作用于虚拟机栈。


来看这么一个方法,

public class study {

    public static void createTarget(){
        target t = new target();
    }

    public static void main(String[] args) throws Exception {
        for(int i=0;i<1000000;i++){
            study.createTarget();
        }
        Thread.sleep(1000000);
    }

}

class target{
}

在主线程中我循环创建一百万个target对象,然后阻塞主线程。之后在cmd命令行中通过jps命令打印虚拟机当前进程号。

Java虚拟机的逃逸分析_第1张图片

当前执行的类对应的进程号为25224,随后利用jmap命令打印该进程在堆中的所有对象。

Java虚拟机的逃逸分析_第2张图片

在程序代码中我是循环创建了100万个target对象,所以在堆中理应有100万个target对象。但打印信息显示只有116738个,减少了将近90万的target对象。这是因为JDK从1.7版本后就默认开启了逃逸分析,对于在方法(无论静态还是实例)中未发生逃逸的对象会被优先考虑栈上分配,利用标量替换的手段将在方法中创建的未发生逃逸的对象存储在栈帧里的局部变量表中,跟随着方法的进栈和出栈。这种在栈上存储的对象,不仅线程安全,而且其随着方法出栈而消亡,具有很高的执行性能。


当然,我们可以在执行之前,设置Jvm虚拟机参数来关闭逃逸分析。

Java虚拟机的逃逸分析_第3张图片

Java虚拟机的逃逸分析_第4张图片

关闭逃逸分析后,在堆上将有100万个target对象,此时并未发生栈上分配。


总结:在以后的编程中,在开启逃逸分析的情况下,尽量在方法内去创建并使用对象。

 

你可能感兴趣的:(java)