垃圾回收器(二):G1

堆空间

  • 自由分区(Free Heap Region,FHR)
  • 新生代分区(Young Hreap Region,YHR)
    • E 是 eden region
    • S 是 survivor region
  • 老年代分区(Old Heap Region,OHR)
    • 如果一个对象占用的空间超过了分区容量50%以上,G1收集器就认为这是一个巨型对象
    • 大对象分区(Humongous Heap Region,HHR)
      • 短期存在的巨型对象
      • G1 只决定它们是否生存,回收他们占用的空间,从不会移动它们
      • 可以跨越多个连续regions。直接分配到老年代,防止反复拷贝移动
      • 在global concurrent marking阶段的cleanup 和 full GC阶段回收
      • 在分配H-obj之前先检查是否超过 initiating heap occupancy percent和the marking threshold, 如果超过的话,就启动global concurrent marking,为的是提早回收,防止 evacuation failures 和 full GC
      • Young-Only 阶段,humongous regions 可能会被回收
      • Space-Reclamation,humongous regions 可能会被回收
      • 为了减少连续H-objs分配对GC的影响,需要把大对象变为普通的对象,建议增大Region size。
      • 一个Region的大小可以通过参数-XX:G1HeapRegionSize设定,取值范围从1M到32M,且是2的指数

GC

  • G1提供了两种GC模式,Young GC和Mixed GC,两种都是完全Stop The World的
  • Young GC:选定所有年轻代里的Region。
  • Mixed GC:选定所有年轻代里的Region,外加根据global concurrent marking统计得出收集收益高的若干老年代Region。在用户指定的开销目标范围内尽可能选择收益高的老年代Region。Mixed GC不是full GC,它只能回收部分老年代的Region
  • 如果mixed GC实在无法跟上程序分配内存的速度,导致老年代填满无法继续进行Mixed GC,就会使用serial old GC(full GC)来收集整个GC heap。G1是不提供full GC的。如果堆是足够大的,Mixed gc 没有回收足够的 old region,或者 concurrent mark 没法及时完成,都可能会导致 full gc。
  • 在几次gc后,old gen 的对象占有比超过了 InitiatingHeapOccupancyPercent,gc就会进入并发标记准备(concurrent mark)。Mixed gc 依赖 concurrent mark
  • G1默认当分区内存占用阈值达到总内存的45%,会发生Mixed gc(混和GC)

GC 流程

YGC流程:

  1. 挂起所有用户线程,应用程序暂时不能向外提供响应(STW)
  2. 计算需要收集的区间(CSet),YGC期间,仅回收Y区,所以所有的Y区就是CSet
  3. 进行根处理,包含JVM根和Java根。为了扫描全堆,这里只需要把RSet记录的对象加入根即可
  4. JIT代码扫描等,根据栈中的对象进行递归遍历复制对象
  5. 因为对象已经有挪动,更新JIT代码中指针存储的对象地址
  6. 引用处理,一些软、弱、虚、析构(FinalReference)引用等的处理
  7. 字符串去重等处理
  8. 清理卡表,也就是把全局卡表中已经处理过的分区的卡表清空
  9. 进行Redirty操作,也就是重构RSet。释放CSet区
  10. 尝试回收大对象,如果某个大对象所在的分区没有RSet引用,说明这个大对象已经死亡,可以直接回收
  11. 尝试扩展内存,参数有GCTimeRatio和GExpandByPercentOfAvailable来决定是否需要扩展,前者在G1中默认值是9,代表的意思是GC占用的时间必须小于1/(1+9),也就是10%,这个值在之前的垃圾收集器默认值是99。如果吞吐量不达标,就会尝试扩展内存,大小由GExpandByPercentOfAvailable决定,默认20,也就是未提交内存的20%
  12. 如果满足条件的,触发并发标记周期;判断方式是YGC之后,老年代内存占总内存超过一定阈值(参数-XX:InitiatingHeapOccupancyPercent决定,默认45%)触发。

混合回收:

  • 根扫描,初始标记:并发标记周期之前一定有一次YGC,所以初始标记会把这次YGC的存活对象也加入根,还有其他根对象,如:栈对象、全局静态对象、JNI对象等等
  • 并发标记(concurrent-mark-start~end之间):因为存活对象会比较多,所以这块耗时会比较久,CMS、G1等的这部肯定都是"并发执行的",要不过长的STW时间,会一定程度上的导致服务运行。并且并发标记过程是涉及所有区的,所有老年代区的对象都参与标记。在标记的过程中也会计算每个区的存活对象数目和字节数。
  • 再标记:再标记过程是有STW。再标记的目的是处理哪些在并发标记过程中有引用关系变更的对象,见"2.c并发标记的难点"。
  • 清理:清理阶段会统计存活对象,并且按照结果对区维度进行排序,以便后续mixed-gc的CSet的选择。同时和会重置RSet,因为老年代的标记已经完成了,这个时候需要删除原来RSet里面保存的引用关系。
相关推荐
u0109147605 小时前
CSS组件库如何快速扩展_通过Sass @extend继承基础布局
jvm·数据库·python
爱敲键盘的猴子5 小时前
JVM -- 内存模型(运行时数据区,垃圾回收机制)
jvm
baidu_340998825 小时前
Golang怎么用go-noescape优化性能_Golang如何使用编译器指令控制逃逸分析行为【进阶】
jvm·数据库·python
m0_678485455 小时前
如何利用虚拟 DOM 实现无痕刷新?基于 VNode 对比的状态保持技巧
jvm·数据库·python
qq_342295825 小时前
CSS如何实现透明背景效果_通过RGBA色彩模式控制透明度
jvm·数据库·python
Greyson15 小时前
CSS如何处理超长文本换行问题_结合word-wrap属性
jvm·数据库·python
justjinji5 小时前
如何批量更新SQL数据表_使用UPDATE JOIN语法提升效率
jvm·数据库·python
weixin_580614006 小时前
MySQL存储过程中如何防止SQL注入_使用参数化查询规范
jvm·数据库·python
2401_837163896 小时前
PHP源码开发用台式机还是笔记本更合适_硬件选型对比【方法】
jvm·数据库·python
baidu_340998826 小时前
mysql修改列名会导致程序报错吗_Change Column语法与兼容性
jvm·数据库·python