1、对象优先在 Eden 区分配
一般情况下,对象都优先在新生代的 Eden 区中分配【有少数对象通过JIT优化之后进行标量替换或者直接在栈上分配】,当 Eden 内存不足时将会发起一次 Minor GC。
2、大对象直接进入老年代
大对象指的是占用大量连续内存的 Java对象,例如长字符串、长数组等,这些大对象的内存分配可能会遭遇总空间充足但连续空间不足而无法分配的情况,或者在复制对象时产生高昂的成本,因此在 Serial 或者 ParNew 虚拟机中可以使用参数 -XX:PretenureSizeThreshold
来设置大对象的阈值,超过此值会直接进入老年代。
3、长期存活对象进入老年代
对于分代收集来说,对象是否进入老年代其中一个衡量值是对象的年龄,在对象头中会有标记对象年龄的计数器,当一次 Minor GC 之后在Eden中存活的对象会被转移到 Survivor 中,此时 Age 为1。对象每在 Survivor 区中熬过一次垃圾收集则 Age+1 ,当年龄达到阈值(默认是15)时会晋升到老年代。
可以通过参数 -XX: MaxTenuringThreshold
来设置年龄阈值。
4、动态对象年龄判定
除了大龄对象晋升机制外,如果在 Survivor 中低于或等于某年龄所有对象大小的总和大于 Survivor 的一半,则大于或等于该年龄的对象可以直接进入老年代,无需达到阈值要求。
举例:如果 Survivor 中 Age<=5 的对象总大小超过了 Survivor 的一半,那么 Age>=5 的对象都可以晋升到老年代中,无需达到阈值15。
5、空间分配担保
假设新生代全部对象都存活,那么 Survivor 的空间可能就不足以容纳了,这时就需要老年代有足够空间去容纳 Survivor 中晋升的对象,因此这里需要老年代做 Survivor 的超额担保。
JDK6 Update 24 之前:
在发生 Minor GC 之前,需要检查老年代连续空间是否大于新生代对象总大小,如果满足则可以直接进行Minor GC,如果不满足则分情况讨论。
① 如果-XX: HandlePromotionFailure=true
则会检查 老年代空间 是否大于 历次新晋老的对象平均大小 ,如果大于则正常进行(尽管这一次Minor GC是有风险的);如果小于则进行一次 Full GC。
② 如果-XX: HandlePromotionFailure=false
则不会执行冒险操作,直接进行 Full GC。
JDK6 Update 24 之后:
只要老年代连续空间大小 大于 新生代对象总大小或者历次晋升对象平均大小,则进行一次 Minor GC ,否则进行 Full GC。