路漫漫之 - jvm G1 垃圾回收器

上篇文章介绍了几种垃圾收回器及堆内存结构,但是我想说的是如果你使用G1 作为垃圾回收器请忽略上篇文章的此图
堆内存结构.png

因为G1的堆内存划分和这不太一样。我就来详细的探讨下G1。G1收集器采用不同的方法来分配堆, G1 将内存分配为 大小为2000对个regions区域,每个取值范围是1到32M,是2的指数。


Yong GC :

当jvm开始运行的时候,新生的对象将会分配到eden regions ,当eden 的region被分配满时将会进行Yong GC
触发G1垃圾收集器进行新生代垃圾收集。新生代收集之后不会有新的分区马上分配到Eden空间,因为这时Eden空间为空。不过会有一个分区分配到Survivor空间(这个例子中,Survivor空间被部分填满),一部分数据会移动到老年代。此处应该有图


gc前.png
gc后.png

G1 把Java堆分为多个Region后,垃圾收集是否就真的能以Region为单位进行了?听起来顺理成章,再仔细想想就很容易发现问题所在:Region不可能是孤立的。一个对象分配在某个Region中,它并非只能被本Region中的其他对象引用,而是可以与整个Java堆任意的对象发生引用关系。那在做可达性判定确定对象是否存活的时候,岂不是还得扫描整个Java堆才能保证准确性? G1解决方式是 使用Remembered Set来避免全堆扫描的。G1中每个Region都有一个与之对应的Re-membered Set,虚拟机发现程序在对Reference类型的数据进行写操作时,会产生一个Write Barrier暂时中断写操作,检查Reference引用的对象是否处于不同的Region之中(在分代的例子中就是检查是否老年代中的对象引用了新生代中的对象),如果是,便通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set之中。当进行内存回收时,在GC根节点的枚举范围中加入Remembered Set即可保证不对全堆扫描也不会有遗漏。


RS.jpg

mixed GC :

  1. G1 特有的混合回收机制,回收内容
  • 所有年轻带的region
    1. eden regions
    2. servivor regions
  • 部分老年代手机收益高的Region根据global concurrent marking统计得出收集收益高的若干老年代Region。在用户指定的开销目标范围内尽可能选择收益高的老年代Region
  1. 触发条件:
    G1HeapWastePercent参数:在global concurrent marking结束之后,我们可以知道old gen regions中有多少空间要被回收,在每次YGC之后和再次发生Mixed GC之前,会检查垃圾占比是否达到此参数,只有达到了,下次才会发生Mixed GC。
    G1MixedGCLiveThresholdPercent:old generation region中的存活对象的占比,只有在此参数之下,才会被选入CSet。
    G1MixedGCCountTarget:一次global concurrent marking之后,最多执行Mixed GC的次数。
    G1OldCSetRegionThresholdPercent:一次Mixed GC中能被选入CSet的最多old generation region数量。
回收前
回收后

Remembered Set

执行流程:

初始标记(initial-mark)。发生STW ,因为它要标记从GC Root直接可达的对象。
并发标记(Concurrent Marking)。 这个阶段完全在后台运行 GC Root开始对heap中的对象标记,这个标记是可中断的,所以这个阶段可能发生新生带的垃圾收集,紧接在标记阶段之后的是重新标记(remarking)阶段和正常的清理阶段
最终标记(Remarking)。标记那些在并发标记阶段发生变化的对象,将被回收。
清除垃圾(Cleanup)。清除空Region(没有存活对象的)。
可以看到G1的收集过程和CMS很相似,但是G1收集器出现碎片化的堆频率却比CMS小的多,因为活跃的对象被移动到另一个分区。

Full GC

  1. 触犯条件:
  • 晋升失败:G1收集器完成了标记阶段,开始启动混合式垃圾回收,清理老年代的分区,不过,老年代空间在垃圾回收释放出足够内存之前就会被耗尽。垃圾回收日志中,这种情况的现象通常是混合式GC之后紧接着一次Full GC。
  • 并发模式失效:G1垃圾收集启动标记周期,但老年代在周期完成之前就被填满,在这种情况下,G1收集器会放弃标记周期
  • 疏散失败:进行新生代垃圾收集时,Survivor空间和老年代中没有足够的空间容纳所有的幸存对象。这种情形在GC日志中通常被当成一种特别的新生代:
  • 疏散失败: 进行新生代垃圾收集时,Survivor空间和老年代中没有足够的空间容纳所有的幸存对象。这种情形在GC日志中通常被当成一种特别的新生代
  • 巨型对象分配失败: 使用G1收集器时,分配非常巨大对象的应用程序可能会遭遇另一种Full GC

注: 发生Full GC时jvm使用的是serial old GC 收集器

GC日志

log
  1. 发生gc时的时间
  2. GC暂停,(疏散暂停)是将活动对象从一个区域(年轻或年轻+old)复制到另一个区域的阶段
  3. 表示一个年轻带的GC活动
  4. 表示GC工作线程的数量
  5. 区域5
    5.1 GC Worker Start (ms):
    收集线程开始的时间,使用的是相对时间,Min是最早开始时间,Avg是平均开始时间,Max是最晚开始时间,
    5.2 Ext Root Scanning (ms)
    扫描Roots花费的时间,Sum表示total cpu time,下同。
    5.3 Update RS (ms): Update RS (ms)是每个线程花费在更新Remembered Set上的时间。
    5.4 Object Copy (ms) 拷贝活的对象到新region的耗时。
    5.5 GC Worker Other (ms):花费在其他工作上(未列出)的时间。
    5.6 GC Worker Total (ms):每个线程花费的时间和。
    5.7 GC Worker End 每个线程结束的时间。
    5.8 Code Root Fixup: 用来将code root修正到正确的evacuate之后的对象位置所花费的时间。
    5.9 Code Root Migration: 更新code root 引用的耗时,code root中的引用因为对象的evacuation而需要更新。
  6. eden 区容量为307M eden区的307M全部被占用,gc后eden区域下降为0,下次eden 扩容到307M(这里不扩容),GC后 Survivours没有增加(0->0), 307.6M(512M)堆的大小为512M使用了307.6M。617.5K指GC后堆内存利用率。
  7. 7所指
    7.1 Sys是进程内核中花费的CPU时间。
    这意味着执行内核中系统调用所花费的CPU时间,而不是仍在用户空间中运行的库代码。
    7.2 user 这只是进程使用的CPU时间。
    7.3 (注意) User + Sys将告诉您进程使用的实际CPU时间。
    请注意,这是跨所有CPU的,因此如果进程有多个线程,它可能会超过Real报告的时间。
    总结:由于G1自己项目里没有使用,介绍G1的资料也不是很多,所以自己写的也不是很好。只能等日后再慢慢完善这篇文章
    参考:《java 性能权威指南》,《深入理解java虚拟机》,https://tech.meituan.com/g1.html
    https://www.slideshare.net/SimoneBordet(需翻墙)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 最近总是在思考,如果不做会计,我还能做什么?
    是立卉啊阅读 1,228评论 0 0
  • 夜很长 也很短 长得 天上的星星数不完 短得 一眨眼 夜色就已然变浅 我的夜 起始于 你慢慢远去的身影 从此 思念...
    哈哈哈哈哈爸爸阅读 1,755评论 2 3
  • 1.Math.floor求一个浮点数的地板,就是求一个最接近它的整数,它的值小于或等于这个浮点数。Math.flo...
    贝灬小晖阅读 1,618评论 0 0
  • 这一次张老师给了一个从情绪中解脱的方法,称为“宽两秒,心自在”,具体为四个步骤。 宽两秒,就是晚两秒。这两秒是用来...
    王卉西安阅读 7,417评论 0 0