分代思想
分代介绍
Java8 时,堆被分为了两份:新生代和老年代(1:2),在 Java7 时,还存在一个永久代
-
新生代使用:复制算法
-
老年代使用:标记 - 清除 或者 标记 - 整理 算法
Minor GC 和 Full GC:
-
Minor GC:回收新生代,新生代对象存活时间很短,所以 Minor GC 会频繁执行,执行的速度比较快
-
Full GC:回收老年代和新生代,老年代对象其存活时间长,所以 Full GC 很少执行,执行速度会比 Minor GC 慢很多
Eden 和 Survivor 大小比例默认为 8:1:1
分代分配
工作机制:
-
对象优先在 Eden 分配:当创建一个对象的时候,对象会被分配在新生代的 Eden 区,当 Eden 区要满了时候,触发 MinorGC
-
当进行 MinorGC 后,此时在 Eden 区存活的对象被移动到 to 区,并且当前对象的年龄会加 1,清空 Eden 区
-
当再一次触发 MinorGC 的时候,会把 Eden 区中存活下来的对象和 to 中的对象,移动到 from 区中,这些对象的年龄会加 1,清空 Eden 区和 to 区
-
To 区永远是空 Survivor 区,From 区是有数据的,每次 MinorGC 后两个区域互换
-
From 区和 To 区 也可以叫做 S0 区和 S1 区
晋升到老年代:
-
长期存活的对象进入老年代:为对象定义年龄计数器,对象在 Eden 出生并经过 Minor GC 依然存活,将移动到 Survivor 中,年龄就增加 1 岁,增加到一定年龄则移动到老年代中
-XX:MaxTenuringThreshold
:定义年龄的阈值,对象头中用 4 个 bit 存储,所以最大值是 15,默认也是 15 -
大对象直接进入老年代:需要连续内存空间的对象,最典型的大对象是很长的字符串以及数组;避免在 Eden 和 Survivor 之间的大量复制;经常出现大对象会提前触发 GC 以获取足够的连续空间分配给大对象
-XX:PretenureSizeThreshold
:大于此值的对象直接在老年代分配 -
动态对象年龄判定:如果在 Survivor 区中相同年龄的对象的所有大小之和超过 Survivor 空间的一半,年龄大于等于该年龄的对象就可以直接进入老年代
空间分配担保:
-
在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的
-
如果不成立,虚拟机会查看 HandlePromotionFailure 的值是否允许担保失败,如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于将尝试着进行一次 Minor GC;如果小于或者 HandlePromotionFailure 的值不允许冒险,那么就要进行一次 Full GC