JVM 之 逃逸分析和TLAB

来看一道面试题:所有的new 对象都是分配在堆上的吗?如果不是,是什么情况。

在没看到这道题目的时候,我对所有对象都分配在堆上的想法是没有一丝怀疑的,但是事实是不一定的。

逃逸分析

逃逸分析的定义

逃逸分析,是一种可以有效减少Java 程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上。

Java在Java SE 6u23以及以后的版本中支持并默认开启了逃逸分析的选项。Java的 HotSpot JIT编译器,能够在方法重载或者动态加载代码的时候对代码进行逃逸分析,同时Java对象在堆上分配和内置线程的特点使得逃逸分析成Java的重要功能。

逃逸分析是如何决定一个对象是否分配在堆

先来看下面这段程序

public class test {

    public static void main(String[] args){
        String str = new String("Test");
        System.out.println(str);
    }
}

花两分钟想想,如果你是jvm的设计师,你会把这个str分配在堆上吗?

jvm中的堆上的对象可以线程共享的,那么我们把str放在堆上,谁来共享呢?

很明显没有人会再使用它,它在自己的线程栈结束的时候,就已经没有意义了。

再来看下面这段程序:

public class test {

    public static String getString(){
        String str = new String("Test");
        return str;
    }

    public static void main(String[] args){
        String MyStr = getString();
        System.out.println(MyStr);
    }
}

在调用getString方法的时候,在该栈帧上,如果我们把str放在栈中,那么调用该方法体将得不到该对象。因为在getString结束的时候,就会把str对象释放,那么在main方法体中,MyStr将会指向null。

于是在这个情况下,str对象是会被放在堆上去共享的。我想看到这,大家已经明白那些对象是可以放在栈上,哪些是必须放在堆上的了。

若一个方法体内的对象不被其他方法或者线程得到,我们可以把对象直接存放在栈上,当JVM能证明一个对象不会逃逸到方法或者线程外,则可能为这个变量进行一些高效的优化。

把对象放在栈上有什么意义

我们知道在堆上的对象是被多个线程共享的,共享就要考虑多线程的安全问题,那么就需要锁的消耗。而且把对象放在栈上,会随着栈的出栈一起释放。减轻GC的压力。

TLAB

JVM在内存新生代Eden Space中开辟了一小块线程私有的区域,称作TLAB(Thread-local allocation buffer)。默认设定为占用Eden Space的1%。在Java程序中很多对象都是小对象且用过即丢,它们不存在线程共享也适合被快速GC,所以对于小对象通常JVM会优先分配在TLAB上,并且TLAB上的分配由于是线程私有所以没有锁开销。因此在实践中分配多个小对象的效率通常比分配一个大对象的效率要高。

但对象还是分配在堆上的,只不过是堆上的一块区域而已。

你可能感兴趣的:(JVM)