对象都是在堆上分配的吗?

学习面向对象的过程中通常的说法是new出来的对象都是分配在堆上的,那么这个结论是不是如此绝对,有没有反例呢?

在Java中,典型的对象不再堆上分配的情况有两种:TLAB栈上分配

一、为什么不在堆上分配

我们知道堆是由所有线程共享的,既然如此那它就是竞争资源,对于竞争资源,必须采取必要的同步,所以当使用new关键字在堆上分配对象时,是需要锁的。既然有锁,就必定存在锁带来的开销,而且由于是对整个堆加锁,相对而言锁的粒度还是比较大的,当对象频繁分配时,不免影响效率。

所以对于某些特殊情况,可以采取避免在堆上分配对象的办法,以提高对象创建和销毁的效率。

二、TLAB分配

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

三、栈上分配

JVM在Server模式下的逃逸分析可以分析出某个对象是否永远只在某个方法、线程的范围内,并没有“逃逸”出这个范围,逃逸分析的一个结果就是对于某些未逃逸对象可以直接在栈上分配,由于该对象一定是局部的,所以栈上分配不会有问题。

四、对象非堆上分配的思想和启发

对象不在堆上分配主要的原因还是堆是共享的,在堆上分配有锁的开销。无论是TLAB还是栈都是线程私有的,私有即避免了竞争(当然也可能产生额外的问题例如可见性问题),这是典型的用空间换效率的做法。

在实践中,类似的做法还有很多,例如Hadoop中对于Map过程在节点的本地内存中处理,直到最后Reduce过程再合并数据。对于任务之间可以分解到不同线程、进程的情况,就可以采用类似的做法用空间换效率,对吞吐率的提升有很大帮助。

对象都是在堆上分配的吗?》上有10条评论

  1. 呵呵,最后一句话没看明白,“对于任务之间可以分解到不同线程、进程的情况,就可以采用类似的做法用空间换效率,对吞吐率的提升有很大帮助。”麻烦解释下,好么?

    [回复]

    Hesey 回复:

    多线程相比单线程同时消耗的内存和资源肯定更多,但是对于适合的任务效率会有提高,这个就是我说的空间换效率~

    [回复]

  2. 栈上分配时若超出栈空间大小怎么处理的

    [回复]

    cenyol 回复:

    不会超出的,在编译和优化的时候就已经计算好了每个函数对应的栈帧的空间大小。是刚刚好的,不多也不少。

    [回复]

发表评论

电子邮件地址不会被公开。