1 对象优先在Eden区中分配
目前主流的垃圾收集器都会采用分代回收算法,因此需要将堆内存分为新生代和老年代.
在新生代中为了防止内存碎片,垃圾收集器一般都选用”复制”算法.因此,堆内存的新生代被进一步分为:Eden区+Survior1区+Survior2区.
每次创建对象时,首先会在Eden区中分配.
若Eden区已满,则在Survior1区中分配.
若Eden区+Survior1区剩余内存太少,导致对象无法放入该区域时,就会启用”分配担保”,将当前Eden区+Survior1区中的对象转移到老年代中,然后再将新对象存入Eden区.
2 大对象直接进入老年代
所谓”大对象”就是指一个占用大量连续内存空间的对象,如很长的字符串及数组.
当发现一个大对象在Eden区+Survior1区中存不下的时候就需要分配担保机制把当前Eden区+Survior1区的所有对象都复制到老年代中去.
一个大对象能够存入Eden区+Survior1区的概率比较小,发生分配担保的概率比较大,而分配担保需要涉及到大量的复制,就会造成效率低下.
因此,对于大对象我们直接把他放到老年代中去,从而就能避免大量的复制操作.
那么,什么样的对象才是”大对象”呢?
- -XX:PretrnureSizeThreshold参数
该参数用于设置大小超过该参数的对象被认为是”大对象”,直接分配在老年代.
注意:该参数只对Serial和ParNew收集器有效.
3 生命周期较长的对象进入老年代
老年代用于存储生命周期较长的对象,那么我们如何判断一个对象的年龄呢?
新生代中的每个对象都有一个年龄计数器,当新生代发生一次MinorGC后,存活下来的被移动到Survivor空间的对象的年龄就加一,在Survivor区每熬过一次MinorGC,年龄就加一,当年龄超过一定值(默认15)时,就将该对象转移到老年代中.
- -XXMaxTenuringThreshold参数
设置该参数后,只要超过该参数的新生代对象都会被转移到老年代中.
4 对象年龄的动态判定
在Survivor空间中,如果年龄相同的对象的内存大小总和超过了Survivor空间的一半,那么所有年龄相同的对象和超过该年龄的对象都会被转移到老年代中.无须等到MaxTenuringThreshold要求的年龄.
5 “分配担保”策略详解
在发生MinorGC前,JVM首先会检查老年代中最大可用的的连续空间是否大于新生代中所有对象的大小.若此条件
- 成立,那么MinorGC可以确保安全进行.
- 不成立,JVM会查看HandlePromotionFailure设置值是否允许担保失败.若允许,继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小
- 若大于,将尝试一次MinorGC,虽然此次MinorGC是有风险的.
- 若小于或HandlePromotionFailure设置不允许冒险,则进行一次FullGC.通过清除老年代中废弃数据来扩大老年代空闲空间,以便给新生代作担保.
注意:
1. 分配担保是老年代为新生代作担保.
2. 新生代中使用”复制”算法实现垃圾回收,老年代中使用’标记-清除”或”标记-整理”算法实现垃圾回收,只有使用”复制”算法的区域才需要分配担保,因此新生代需要分配担保,而老年代不需要分配担保.