好好理解一下G1收集器

来源:互联网 发布:java公钥加密私钥解密 编辑:程序博客网 时间:2024/06/06 09:27

G1收集器是Java1.7新提供的收集器,G1收集器的开发意在在未来的一天,可以取代CMS。但是到目前为止,(2017.08)Java的默认收集器依然是CMS那一套,当然目前很多消息是在Java9上,G1会成为JVM的默认收集器
G1收集器要解决的核心问题,是让STW可预测,为了实现这个目标,采取了和传统方式区别开来的地方:
1.G1的内存模型略有变化,将一块内存模型分成多个Region管理,其中,每块Region大小是在JVM启动的时候就固定的,使用参数-XX:G1HeapRegionSize设定,范围为1M~32M
这里写图片描述
上图可知,G1同样会划分Eden Survice 和Old,但不同的是G1的分代只是逻辑上划分,在实际内存中完全是可以不连续的。
有一些Region标明了H,它代表Humongous Object(简称H-Obj),这表示这些Region存储的是巨大对象,即大小大于等于region一半的对象。H-obj直接分配到了old ,防止了反复拷贝移动。 对于H-obj的GC,无疑是很费时间的,G1的G1HeapRegionSize参数目的就是为了让开发者将Region块大小调节到一个最适合自己的数值,从而减少H-obj

将堆内存分块管理似乎是可以让堆内存的管理,分配,收集以块为单位进行,但实际上复杂的多。Region和Region之间是有关联的,某一个Region中的对象,很有可能引用着另一个Region中的对象,因此,在GC的标记阶段,还是需要扫描整个堆才可以?
对于这个问题,G1采取了空间换时间的方式:
G1在内存中维护了一个Remembered Set(简称RSet),每个块都有自己的RSet,里面记录的就是当前Region对其他Region的引用信息,在GC的时候,标记阶段同时会检查RSet是否引用了其他Region的对象,如果有,就去其他的Region继续标记,如果没有,标记会停止。
同时注意到,RSet是需要额外内存的,并且Region越多,内存的额外开销就越大,因此,在G1收集器下,设置合理的G1HeapRegionSize很重要

2.可预测停顿模型
听着就非常的高端,然而实际上确实也很高端。G1收集器有一个参数-XX:MaxGCPauseMillis,功能是可以设置G1收集过程停顿时间,默认值是200ms,就是说,你这一个参数配了下去,G1可以在你规定的时间内完成收集?这么吊?那我设置0是不是就不会STW了?
首先,这个值只是一个期望,G1表示他会尽力达到这个停顿时间,实际上,这个值设的太小也是会有问题的,先理解下这个高端的可预测停顿模型的原理就知道问题在哪了
同一个程序,在不考虑GC自身,在代码运行时产生的垃圾不随GC收集器的变化而变化,也就是垃圾的数量是一定的,具体怎么收集,何时收集,要看GC的实现,而G1即是,为了满足开发者配置的MaxGCPauseMillis,而对垃圾进行取舍的收集,也就是说,如果MaxGCPauseMillis配置的过低,每次GC都只能收集G1认为耗时较小的垃圾,而放弃收集其他耗时较长的垃圾,如此堆积下去,会造成更多的次数的GC,或是G1无视了MaxGCPauseMillis耗时更久,甚至当G1表示妥协,用Serial Old收集器去收集,造成更长时间的STW
也就是说,G1收集器实现可预测的停顿,基本思想是通过给垃圾标记优先级,来进行最优化的收集,那么如何实现的呢?
除了RSet之外,G1还维护了一个列表,Collcetion Set(简称CSet),使用CSet来存储要收集的垃圾,并标识了优先级,而标识优先级使用的是衰减标准偏差为理论基础实现的(算法以后再研究吧,总之这是一个可以根据经验来判断收集耗时的算法)。
下图为G1收集大致流程图
这里写图片描述
初始标记即在只标记GC Root直接对应的节点,此时需要STW
并发标记和用户线程并行执行,并遍历初始标记的节点,并且记录这段运行期间的RSet的变更
最终标记会将RSet变更合并到RSet中,此时需要STW,并取得所有要收集的垃圾
筛选回收阶段首先对各个Region的回收价值和成本进行排序(维护CSet),根据用户所期望的GC停顿时间来制定回收计划,此时需要STW。

G1提供的GC机制
Young GC:选定所有新生代里的Region。通过控制新生代的region个数,来控制young GC的时间开销。
Mixed GC:选定所有新生代里的Region,外加根据统计得出收集收益高的若干老年代Region。在用户指定的开销目标范围内尽可能选择收益高的老年代Region。
当G1对于过大增长的内存无能为力的时候,G1适用serial GC来收集整个堆。

另外附加几个G1收集器可用的参数:
-XX:G1HeapRegionSize=n 设置Region大小
-XX:MaxGCPauseMillis 设置G1收集过程停顿时间,默认值200ms,非硬性条件
-XX:G1NewSizePercent 新生代最小值,默认值5%
-XX:G1MaxNewSizePercent 新生代最大值,默认值60%
-XX:ParallelGCThreads StopTheWorld期间,并行GC线程数
-XX:ConcGCThreads=n 并发标记阶段,并行执行的线程数
-XX:InitiatingHeapOccupancyPercent 设置触发标记周期的 Java 堆占用率阈值。默认值是45%。这里的java堆占比指的是非新生代可用字节,包括H-obj

原创粉丝点击