在虚拟机中,我们知道对象的内存是分配在堆中的。但是堆又可以划分为更小的区域以便垃圾回收,那么,对象到底是怎么在分配在堆中的呢?
一:对象优先分配在线程的本地分配缓冲区
在前面我们提到,每个线程可以在堆中预先分配得到一片区域,作为本地线程分配缓冲区(TLAB)。当该线程执行时,有对象创建的话,就在该线程的TLAB中分配内存。当该线程的TLAB用完了才申请堆中的空闲内存。
二:堆中优先分配Eden
大多数情况下,对象都在新生代的Eden区中分配内存。而因为大部分的对象都是“朝生夕死”的,所以新生代又会频繁进行垃圾回收。
三:大对象直接进入老年代
需要大量连续空间的对象,如:长字符串、数组等,会直接在老年代分配内存。这是因为,这样可以避免在新生代区频繁的GC时发生大量的内存赋值(新生代的GC是采用复制算法的)。
四:长期存活的对象“晋入”老年代
新生代中经历了多次GC仍然存活的对象,当年龄达到一定程度(默认15)时就会晋升到老年代。
为了更好地适应内存情况,虚拟机不是要求对象必须到达阀值才可晋升老年代的,而是采用动态年龄判定的方法:如果Servivor空间中相同年龄的对象大小大于Servivor空间的一般时,由于下一次的MinorGC时,这些对象如果仍然存活的话,复制到ToServivor空间时就放不下了。所以,在本次GC时就可以把这些对象以及年龄大于等于这些对象的直接进入老年代。
在MinorGC时,如果Eden和FromServivor中存活的对象在复制到ToServivor时放不下了,也会直接分配到老年代。
五:空间分配担保
在MinorGC之前,会先检查老年代最大可用空间是否可以容纳新生代所有对象(防止新生代全部晋升时放不下),如果可以容纳,则MinorGC可以安全执行。否则,检查是否允许担保失败,是则检查老年代最大可用空间是否大于历次晋升到老年代的对象的平均大小,是则尝试进行MinorGC;小于或者MinorGC失败,则会发起一次FullGC清理老年代