搞懂G1垃圾收集器
一.G1 GC术语Overview
1.1 并发
并发的意思是Java应用执行和垃圾收集活动可以同时进行
1.2 并行
并行的意思是垃圾收集运算是多线程执行的,比如CMS垃圾收集器的年轻代就是并行的,并行与串行的区别如下图,左边为串行,右边为串行:

1.3 STW
STW(stop the world)意思是在一个垃圾回收事件中,所有Java应用线程会被暂停。只有暂停,应用才不会产生新的垃圾,有益于垃圾收集器更好的标记垃圾对象。(这就像是你在家扫狗毛,肯定要把狗先关笼子,停止它的活动)
1.4 Region
请先忘记这个图,学习G1过程中不会对每个代进行设置了

G1垃圾收集器利用分而治之的思想将堆进行分区,划分为一个个的区域。每次收集的时候,只收集其中几个区域,以此来控制垃圾回收产生的STW
G1和其他GC算法最大的区别是弱化分代概念,引入分区思想!!!

如果要另外选择分区的尺寸,可以通过命令行选项:-XX:G1HeapRegionSize=n中进行设置
1.5 RSet
G1垃圾收集器里每一个RSet对应的是一个Region内部对象引用情况,说白了就是存在Region中存活对象的指针。在标记存活对象的时候,G1使用RSet概念,将每个分区指向分区内的引用记录在该分区,避免对整个堆扫描,并行独立处理垃圾集合
- 老年代对年轻代的引用,维护老年代分区指向年轻代分区的指针
- 老年代对老年代的引用。在这里,老年代中不同分区的指针将被维护在老年代拥有分区的RSet中
如下图,我们可以看到3各分区,x(年轻代分区)、y和z(老年代分区)。x有一个来自z的对内引用。这个引用记录在x的RSet中,分区z有2个对内引用,一个来自x一个来自y,因为年轻代分区作为一个整体回收的,所以只需记录来自y的对内引用,不用记录x的对内引用

1.6 CSet
Collection Set,简称CSet。在垃圾收集过程中收集的Region集合可以称为收集集合(CSet),也就是在垃圾收集暂停过程中被回收的目标。GC时在CSet中的所有存活数据都会被转移,分区释放回空闲分区队列
见下图,左边的年轻代收集CSet代表年轻代的一部分分区,右边的混合收集CSet代表年轻代的一部分区和老年代的多个分区:

1.7 PLAB
Promotion Local Allocation Buffers,对象晋升到survivor分区或者老年代分区的过程是在GC线程的晋升本地分配缓冲区(PLAB)进行的,每个线程有独立的PLAB。作用是避免多线程竞争相同数据。和下面介绍的TLAB思想是一致的
1.8 TLAB
Thread Local Allocation Buffers,线程本地分配缓存。JVM使用了TLAB这种线程专属的区间来避免多线程冲突(无锁方式),提高对象分配效率。TLAB本身占用了Eden空间,即JVM会为每一个线程都分配一块TLAB空间

1.9 IHOP
InitiatingHeapOccupancyPercent,简称IHOP。缺省情况是Java堆内存的45%。当老年代的空间超过45%,G1会启动一次混合周期收集
这也是G1和CMS之间较大的区别,G1的百分比是相对于整个Java堆而言的,CMS(CMSInitiatingOccupancyFraction)仅仅是针对老年代空间的占比。
为什么G1如此设计???
因为G1没有固定物理上分割一块内存作为老年代,而是用了Region的思想,这些Region可能是eden,survivor、老年代或者巨型分区,所以获取针对老年代本身的占用百分比没有意义
2.10 巨型分区
巨型对象会以连续分区的形式来存放,这种就叫巨型分区。巨型对象无法利用年轻代里的TLAB和PLAB。在JDK 8u40之前,它只能在并发收集周期的清除阶段回收,但是在JDK 8u40之后,巨型分区可以在年轻代收集中和full GC被回收
二.G1的设计
2.1 为什么会有G1?
为什么会有G1呢?因为并发、并行和CMS垃圾收集器都有2个共同的问题:
- 老年代收集器大部分操作都必须扫描整个老年代空间(标记,清除和压缩)。这就导致了GC随着Java堆空间而线性增加或减少
- 年轻代和老年代是独立的连续内存块,所以要先决定年轻代和年老代放在虚拟地址空间的位置
2.2 Region的设计
上面说到,G1垃圾收集器利用分而治之的思想将堆进行分区,划分为一个个的区域。G1垃圾收集器将堆拆成一系列的分区,这样的话,大部分的垃圾收集操作就只在一个分区内执行,而不是整个堆或者整个代

2.3 设计目标
G1的设计目标就是把必要的调整限定在以下2个:
- 设置最大的Java堆空间
- 设置指定GC暂停时间
G1会通过调整Java堆尺寸大小来满足设定的暂停时间目标,暂停时间目标越短,年轻代空间越小,老年代空间相对越大
2.4 使用场景
G1 GC切分堆内存为多个区间(Region),从而避免很多GC操作在整个Java堆或者整个年轻代进行。G1 GC只关注你有没有存货对象,都会被回收并放入可用的Region队列。G1 GC是基于Region的GC,适用于大内存机器。即使内存很大,Region扫描,性能还是很高的
如果现在采用的收集器没有问题,就不要选择G1,如果追求低停顿,那么G1已经是一个可尝试的选择,如果追求吞吐量,就不要选G1了
四.G1的垃圾回收
G1的垃圾收集周期主要有4种类型:年轻代收集周期、多级并发标记周期、混合收集周期和full GC(转移失败的安全保护机制)
这一节我会以应用启动的时间顺序来讲,这样比较易懂一点,也可以参照G1垃圾收集活动时序图:

4.1 年轻代收集
应用刚启动,慢慢流量进来,开始生成对象。G1会选一个分区并指定他为eden分区,当这块分区用满了之后,G1会选一个新的分区作为eden分区,这个操作会一直进行下去直到达到eden分区上限,也就是说eden分区已经被占满,那么会触发一次年轻代收集
年轻代收集首先做的就是迁移存活对象,它使用单eden,双survivor进行复制算法,它将存活的对象从eden分区转移到survivor分区,survivor分区内的某些对象达到了任期阈值之后,会晋升到老年代分区中。原有的年轻代分区会被整个回收掉
同时,年轻代收集还负责维护对象年龄,存活对象经历过年轻代收集总次数等信息。G1将晋升对象的尺寸总和和它们的年龄信息维护到年龄表中,结合年龄表、survivor占比(--XX:TargetSurvivorRatio 缺省50%)、最大任期阈值(--XX:MaxTenuringThreshold 缺省为15)来计算出一个合适的任期阈值
调优:我们可以通过--XX:MaxGCPauseMillis,调优年轻代收集,缩小暂停时间
4.2 并发标记周期
随着时间推移,越来越多的对象晋升到老年代中,当老年代占比(相对于Java总堆而言)达到IHOP参数(上图的IHOP Trigger)之后,那么G1首先会触发并发标记周期(上图的Concurrent Marking Cycle),当完成后才会开始下一小节的混合垃圾收集周期
G1的并发标记循环分5个阶段:
第一阶段:初始标记(上图Young Collection with Initial Mark),收集所有GC根(对象的起源指针,根引用),STW,在年轻代完成
第二阶段:根区间扫描,标记所有幸存者区间的对象引用
第三阶段:并发标记(上图Concurrent Marking),标记存活对象
第四阶段:重新标记(上图Remark),是最后一个标记阶段,STW,很短,完成所有标记工作
第五阶段:清除(上图Clean),回收没有存活对象的Region并加入可用Region队列
调优:我们可以通过--XX:InitiatingHeapOccupancyPercent,配置适合应用的IHOP值(过大会可能转移失败,过小可能过早引起并发标记周期)
我们也可以通过--XX:ConcGCThreads,增加并发线程数
4.3 混合收集周期
当达到IHOP参数并完成上一小节的并发标记周期之后,混合收集周期就启动了,一个周期里的单次STW的混合收集和年轻代收集是类似的,唯一区别就是在混合收集过程中会包含一部分老年分区,所以也叫混合收集
看上图的Mixed Collection Cycle,中间有好几段Mixed Collection,说明混合收集周期包含多次收集次数。那么什么影响收集次数呢?是固定的?还是?有两个参数比较重要:
-XX:G1MixedGCCountTarget:缺省值为8,意思是能启动混合收集的数目设定一个物理限制。G1根据将回收的老年分区除以该参数值得到每次混合收集的老年代CSet最小数量
-XX:G1HeapWastePercent:缺省值为5%,每次混合收集暂停,G1算出废物百分比,根据堆废物百分比,当收集达到参数时,不再启动新的混合收集
调优:当暂停时间和运行时间呈现指数级增长,可以通过-XX:G1HeapWastePercent,调高该参数会有所帮助,但这也导致更多碎片化
4.4 full GC
有2个条件同时满足则会触发full GC
1.拷贝存活对象晋升(promotion)失败,无法找到可用的空闲分区,GC日志记录为to-space exhausted。或者分配巨型对象无法在老年代找到连续足够的分区

2.当发生第一个条件后,G1会尝试增加堆使用量,如果扩展失败,那么会触发安全措施机制同时发生full GC
full GC中,单个线程会对整个堆的所有代中所有分区做标记、清除以及压缩动作!!非常非常昂贵的操作!
搞懂G1垃圾收集器的更多相关文章
- 一篇文章搞懂G1收集器
一.何为G1收集器 The Garbage-First (G1) garbage collector is a server-style garbage collector, targeted for ...
- 深入理解 Java G1 垃圾收集器--转
原文地址:http://blog.jobbole.com/109170/?utm_source=hao.jobbole.com&utm_medium=relatedArticle 本文首先简单 ...
- G1 垃圾收集器
概念先知 什么是垃圾回收 简单的说垃圾回收就是回收内存中不再使用的对象. 垃圾回收的基本步骤: 查找内存中不再使用的对象 释放这些对象占用的内存 查找内存中不再使用的对象 如何判断哪些对象不再被使用呢 ...
- G1垃圾收集器和CMS垃圾收集器 (http://mm.fancymore.com/reading/G1-CMS%E5%9E%83%E5%9C%BE%E7%AE%97%E6%B3%95.html#toc_8)
参考来源 JVM 体系架构 堆/栈的内存分配 静态和非静态方法的内存分配 CMS 回收算法 应用场景 CMS 垃圾收集阶段划分(Collection Phases) CMS什么时候启动 CMS缺点 G ...
- 【JVM】7、深入理解Java G1垃圾收集器
本文首先简单介绍了垃圾收集的常见方式,然后再分析了G1收集器的收集原理,相比其他垃圾收集器的优势,最后给出了一些调优实践. 一,什么是垃圾回收 首先,在了解G1之前,我们需要清楚的知道,垃圾回收是什么 ...
- G1 垃圾收集器入门
最近在复习Java GC,因为G1比较新,JDK1.7才正式引入,比较艰难的找到一篇写的很棒的文章,粘过来mark下.总结这篇文章和其他的资料,G1可以基本稳定在0.5s到1s左右的延迟,但是并不能保 ...
- 转 G1垃圾收集器入门
转自:http://blog.csdn.net/zhanggang807/article/details/45956325 最近在复习Java GC,因为G1比较新,JDK1.7才正式引入,比较艰难的 ...
- G1垃圾收集器的实现原理
(G1垃圾收集器的实现原理.G1和CMS经常被单独拎出来问) https://tech.meituan.com/g1.html G1太复杂,说下CMS吧
- 详解 JVM Garbage First(G1) 垃圾收集器(转载)
前言 Garbage First(G1)是垃圾收集领域的最新成果,同时也是HotSpot在JVM上力推的垃圾收集器,并赋予取代CMS的使命.如果使用Java 8/9,那么有很大可能希望对G1收集器进行 ...
随机推荐
- 源代码管理工具(1)——SVN(1)——SVN 的使用新手指南,具体到步骤详细介绍----TortoiseSVN
一.客户端的使用: TortoiseSVN(下载地址:https://pan.baidu.com/s/14cAEV5ZfMA9mLlQAb4oznw 这里有包含中文版的语言包). 1.先下载安装(这个 ...
- 201771010135杨蓉庆 《面向对象程序设计(java)》第三周学习总结
一:第1-3章学习内容: 第一章:复习基本数据类型 整型 byte(1个字节 表示范围:-2^7 ~ (2^7)-1) short(2个字节 表示范围:-2^15~(2^15)-1) int(4个字节 ...
- 解决chrome记住账号默认样式覆盖
当谷歌游览器记住账号后,输入框的颜色会变为黄色,最直接的方法:加入以下代码 input:-webkit-autofill , textarea:-webkit-autofill, select:-we ...
- Euler Sums系列(二)
\[\Large\sum_{n=0}^\infty \frac{H_{2n+1}}{(2n+1)^2}=\frac{21}{16}\zeta(3)\] \(\Large\mathbf{Proof:}\ ...
- 重新梳理IT知识之java-01语法(一)
标识符的命名规范 包名:xxxyyyzzz 类名.接口名:XxxYyyZzz (大驼峰) 变量名.方法名:xxxYyyZzz 常量名:XXX_YYY_ZZZ //**************强制类型转 ...
- linux可执行程序调试
gdb调试不用多说 ./testapp 2>%261 适合线上问题排查,可打印程序错误接口代码 ldd testapp 查看代码动态链接程序是否正常
- [运维] 如何解决 nginx: [emerg] bind() to 0.0.0.0:80 failed (13: Permission denied)
环境: 虚拟机 linux centos 7 64 当时正在配置 nginx , 由于解压后的 nginx 默认安装位置是在 /usr/local/ 目录下, 而这个目录是 root 用户才有权限操作 ...
- 如何用 pycharm 调试 airflow
airflow 和 pycharm 相关基础知识请看其他博客 我们在使用 airflow的 dag时. 每次写完不知道对不对的,总不能到页面环境中跑一下,等到报错再调试吧.这是很让人恼火的事情 这里我 ...
- Hibernate面试题(七)--load与get区别
1. load方式是延迟加载,只有属性被访问的时候才会调用sql语句 get方式是非延迟加载,无论后面的代码是否会访问到属性,马上执行sql语句 2. 都通过id=500去获取对象1. get方式会返 ...
- WEB安全 - XSS,CSRF
1. CSRF参考 https://www.ibm.com/developerworks/cn/web/1102_niugang_csrf/ https://en.wikipedia.org/wiki ...