一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情。
(4)G1
-
定义: Garbage First
-
JDK 9以后默认使用,而且替代了CMS 收集器
-
适用场景
- 同时注重吞吐量和低延迟(响应时间)
- 超大堆内存(内存大的),会将堆内存划分为多个大小相等的区域
- 整体上是标记-整理算法,两个区域之间是复制算法
-
相关参数,JDK8 并不是默认开启的,所需要参数开启
a. G1 垃圾回收阶段
新生代伊甸园垃圾回收—–>内存不足,新生代回收+并发标记—–>回收新生代伊甸园、幸存区、老年代内存 ——>新生代伊甸园垃圾回收(重新开始)
b.Young Collection
分区算法region
分代是按对象的生命周期划分,分区则是将堆空间划分连续几个不同小区间,每一个小区间独立回收,可以控 制一次回收多少个小区间,方便控制 GC 产生的停顿时间
E:伊甸园 S:幸存区 O:老年代
会STW
c.Young Collection + CM
CM:并发标记
-
在 Young GC 时会对 GC Root 进行初始标记
-
在老年代占用堆内存的比例达到阈值时,对进行并发标记(不会STW),阈值可以根据用户来进行设定
d.Mixed Collection
会对E S O 进行全面的回收
- 最终标记
- 拷贝存活
-XX:MaxGCPauseMills:xxx 用于指定最长的停顿时间
为什么有的老年代被拷贝了,有的没拷贝?
因为指定了最大停顿时间,如果对所有老年代都进行回收,耗时可能过高。为了保证时间不超过设定的停顿时 间,会回收最有价值的老年代(回收后,能够得到更多内存)
e.Full GC
G1在老年代内存不足时(老年代所占内存超过阈值)
- 如果垃圾产生速度慢于垃圾回收速度,不会触发Full GC,还是并发地进行清理
- 如果垃圾产生速度快于垃圾回收速度,便会触发Full GC
f.Young Collection 跨代引用
新生代回收的跨代引用(老年代引用新生代)问题
-
卡表与Remembered Set
- Remembered Set 存在于E中,用于保存新生代对象对应的脏卡
- 脏卡:O被划分为多个区域(一个区域512K),如果该区域引用了新生代对象,则该区域被称 为脏卡
-
在引用变更时通过post-write barried + dirty card queue
-
concurrent refinement threads 更新 Remembered Set
g.Remark
重新标记阶段
在垃圾回收时,收集器处理对象的过程中
黑色:已被处理,需要保留的 灰色:正在处理中的 白色:还未处理的
但是在并发标记过程中,有可能A被处理了以后未引用C,但该处理过程还未结束,在处理过程结束之前A引用 了C,这时就会用到remark
过程如下
- 之前C未被引用,这时A引用了C,就会给C加一个写屏障,写屏障的指令会被执行,将C放入一个队列当 中,并将C变为 处理中 状态
- 在并发标记阶段结束以后,重新标记阶段会STW,然后将放在该队列中的对象重新处理,发现有强引用 引用它,就会处理它
h.JDK 8u20 字符串去重
过程
-
将所有新分配的字符串(底层是char[])放入一个队列
-
当新生代回收时,G1并发检查是否有重复的字符串
-
如果字符串的值一样,就让他们引用同一个字符串对象
-
注意,其与String.intern的区别
- intern关注的是字符串对象
- 字符串去重关注的是char[]
- 在JVM内部,使用了不同的字符串标
优点与缺点
- 节省了大量内存
- 新生代回收时间略微增加,导致略微多占用CPU
i.JDK 8u40 并发标记类卸载
在并发标记阶段结束以后,就能知道哪些类不再被使用。如果一个类加载器的所有类都不在使用,则卸载它所 加载的所有类
j. JDK 8u60 回收巨型对象
- 一个对象大于region的一半时,就称为巨型对象
- G1不会对巨型对象进行拷贝
- 回收时被优先考虑
- G1会跟踪老年代所有incoming引用,如果老年代incoming引用为0的巨型对象就可以在新生代垃圾回收 时处理掉
k.JDK 9 并发标记起始时间的调整
-
并发标记必须在堆空间占满前完成,否则退化为 FullGC
-
JDK 9 之前需要使用 -XX:InitiatingHeapOccupancyPercent
-
JDK 9 可以动态调整
- -XX:InitiatingHeapOccupancyPercent 用来设置初始值
- 进行数据采样并动态调整
- 总会添加一个安全的空档空间
l.JDK 9 更高效的回收
- 250+增强
- 180+bug修复
- docs.oracle.com/en/java/jav…