JAVA之G1与CMS垃圾回收
G1 GC,全称Garbage-FirstGarbage Collector,通过-XX:+UseG1GC参数来启用,作为体验版随着JDK 6u14版本面世,在JDK 7u4版本发行时被正式推出,相信熟悉JVM的同学们都不会对它感到陌生。在JDK 9中,G1被提议设置为默认垃圾收集器(JEP 248)。那么与之前的CMS相比,G1有哪些改变,哪些优势呢?
什么是CMS
CMS收集器是基于标记清除算法的一种并发的,低停顿的收集器,值得注意的一点是,CMS只是低停顿而不是没有停顿
CMS分为以下四步:
l 初始标记
l 并发标记
l 重新标记
l 并发清除
初始标记和重新标记都是需要Stop the world的。
初始标记仅仅是记录CG root关联的对象,因此停顿时间比较短,并发标记是进行GC RootTracing,重新标记是修正并发标记期间标记的改动(时间比初始标记稍长一点),之后就是进行并发清除, 由于最耗时间的工作都是在并发操作中完成的,所以CMS的停顿会比较低
下面是CMS的过程图
CMS是一个优秀的垃圾回收器,但是他也有不少缺点:
l 他没有办法处理浮动垃圾(就是在初始标记之后产生的垃圾)
l 他会占用大量的CPU资源
l 他会产生碎片空间(当发生FullGC时会使用Serial Old回收器来处理碎片空间)
l 他在回收垃圾时,由于是并行的收集,所以需要的空间比较大
什么是G1
G1是一个并行回收器,它把堆内存分割为很多不相关的区间,每个区间可以属于老年代或者年轻代,并且每个年龄代区间可以是物理上不连续的。老年代区间这个设计理念本身是为了服务于并行后台线程,这些线程的主要工作是寻找未被引用的对象,而这样就会产生一种现象,即某些区间的垃圾(未被引用对象)多于其它的区间。垃圾回收时都是需要停下应用程序的,不然就没办法防止应用程序的干扰。G1 GC可以集中精力在垃圾最多的区间上,并且只费一点点时间就可以清空这些区间的垃圾,腾出完全空闲的区间。由于这种方式的侧重点在于处理垃圾最多的区间,所以我们给G1一个名字:垃圾优先(Garbage First)。
G1内部有四个操作阶段:
l 年轻代回收;(AYoung Collection)
l 运行在后台的并行循环;(ABackground,Concurrent Cycle)
l 混合回收;(A MixedCollection)
l 全量回收;(A FullGC)
CMS与G1的分析比较
分代收集
这个现在是垃圾回收器的标配,G1和CMS也不例外。但是G1同时回收老年代和年轻代,而CMS只能回收老年代,需要配合一个年轻代收集器。另外G1的分代更多是逻辑上的概念,G1将内存分成多个等大小的region,Eden/ Survivor/Old分别是一部分region的逻辑集合,物理上内存地址并不连续。
CMS在old gc的时候会回收整个Old区,对G1来说没有old gc的概念,而是区分Fullyyoung gc和Mixed gc,前者对应年轻代的垃圾回收,后者混合了年轻代和部分老年代的收集,因此每次收集肯定会回收年轻代,老年代根据内存情况可以不回收或者回收部分或者全部(这种情况应该是可能出现)。
如何处理跨代引用
在垃圾回收的时候都是从Root开始搜索,这会先经过年轻代再到老年代,对于年轻代引用老年代的这种跨代不需要单独处理。但是老年代引用年轻代的会影响young gc,这种跨代需要处理。
为了避免在回收年轻代的时候扫描整个老年代,需要记录老年代对年轻代的引用,young gc的时候只要扫描这个记录。CMS和G1都用到了Card Table,但是用法不太一样。JVM将内存分成一个个固定大小的card,然后有一个专门的数据结构(即这里的Card Table)维护每个Card的状态,一个字节对应一个Card,有点像内存page的概念,只是page是硬件上的,Card Table是软件上的。当一个Card上的对象的引用发生变化的时候,就将这个Card对应的Card Table上的状态置为dirty,young gc的时候扫描状态是dirty的Card即可。这是基本的用法,CMS基本上就是这么使用。
G1在Card Table的基础上引入的rememberedset(下面简称RSet)。每个region都会维护一个RSet,记录着引用到本region中的对象的其他region的Card。比如A对象在regionA,B对象在regionB,且B.f = A,则在regionA的RSet中需要记录B所在的Card的地址。这样的好处是可以对region进行单独回收,这要求RSet不只是维护老年代到年轻代的引用,也要维护这老年代到老年代的引用,对于跨代引用的每次只要扫描这个region的RSet上的Card即可。
上面说过年轻代到老年代的引用不需要单独处理,这带来了很大的性能上的提升,因为年轻代的对象引用变化很大,如果都需要记录下来成本会很高。同时也说明只需要在老年代维护Card Table。
如何处理并发过程的对象变化
CMS和G1都有并发处理过程,这个过程应用程序跟着gc线程一起运行,会产生新对象,也会有旧的对象死去,对象之间的引用关系也会发生变化。这部分数据可以暂时不处理,留到下一次再处理吗?如果可以这样的话问题就会变得很简单,但是答案是不行。考虑下图的场景(图中每一行表示一个内存状态,每一列表示一个Card,这里有4个):第一步a是并发标记中途的一个状态,标记了a b c e四个对象,0 1两个Card已经标记好;第二步b并发标记的同时引用发生变化,g不再指向d,而b不再指向c,变成指向d,这个时候处理Card 2,会标记到g,然后就标记结束了,导致d对象丢失。
CMS初始标记的时候会标记所有从root直接可达的对象,并发标记的时候再从这些对象进一步搜索其他可达对象,最终构成一个存活的对象图。并发标记过程中引用发生变化的也是通过Card Table来记录。但是young gc的时候如果一个dirty card没有包含到年轻代的引用,这个card会重新标记为clean,这有可能将并发标记过程产生的dirty card错误清除,因此CMS引入了另一个数据结构mod union table,这里一个bit对应一个Card,young gc在将Card Table设置为clean的时候会将对应的mod union table置为dirty。最终标记的时候会将Card Table或者mod union table是dirty的Card也作为root去扫描,从而解决并发标记过程产生的引用变化。CMS还需要处理并发过程从年轻代晋升到老年代的对象,处理方式是将这部分对象也作为root去扫描。
G1使用一个称为snapshot at thebeginning(下面简称SATB)的算法,在初始标记的时候得到一个从root直接可达的snapshot,之后从这个snapshot不可达的对象都是可以回收的垃圾,并发过程产生的对象都默认是活的对象,留到下一次再处理。对于引用关系发生变化的,将这个对象对应的Card放到一个SATB队列里,在最终标记的时候进行处理(如果超过一定的阈值并发标记的时候也会处理一部分),处理的过程就是以队列中的Card作为root进行扫描。
Write Barrier
Write Barrier可以理解为在写的时候插入一条特定的操作。
在CMS中老年代引用年轻代的时候就是通过触发一个Write Barrier来更新Card Table的标志位。这是一个同步操作,在更新引用的时候顺带执行,只需要两个指令,引入的消耗不大。
G1比较复杂,在两个地方用到了WriteBarrier,分别是更新RSet的rememberd set Write Barrier和记录引用变化的ConcurrentMarking Write Barrier,前者发生在引用更新之后,称为Post Write Barrier,后者发生在引用变化之前,称为Pre Write Barrier。G1为了提高性能,这两个Write Barrier都是先放到队列中,再异步进行处理。
Full GC
导致CMS Full GC的可能原因主要有两个:Promotion Failure和Concurrent Mode Failure,前者是在年轻代晋升的时候老年代没有足够的连续空间容纳,很有可能是内存碎片导致的;后者是在并发过程中jvm觉得在并发过程结束前堆就会满了,需要提前触发Full GC。CMS的Full GC是一个多线程STW的Mark-Compact过程,,需要尽量避免或者降低频率。
G1的初衷就是要避免Full GC的出现,Full GC会会对所有region做Evacuation-Compact,而且是单线程的STW,非常耗时间。导致G1Full GC的原因可能有两个:1. Evacuation的时候没有足够的to-space来存放晋升的对象;2. 并发处理过程完成之前空间耗尽。这两个原因跟CMS类似。
————————————————
版权声明:本文为CSDN博主「HelloWorld搬运工」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wufaliang003/article/details/80684379
JAVA之G1与CMS垃圾回收的更多相关文章
- CMS垃圾回收与G1垃圾回收
CMS垃圾回收与G1垃圾回收的比较请参见:http://colobu.com/2015/04/14/G1-Getting-Started/
- Java虚拟机内存模型及垃圾回收监控调优
Java虚拟机内存模型及垃圾回收监控调优 如果你想理解Java垃圾回收如果工作,那么理解JVM的内存模型就显的非常重要.今天我们就来看看JVM内存的各不同部分及如果监控和实现垃圾回收调优. JVM内存 ...
- Java进阶 JVM 内存与垃圾回收篇(一)
JVM 1. 引言 1.1 什么是JVM? 定义 Java Vritual Machine - java 程序的运行环境(Java二进制字节码的运行环境) 好处 一次编译 ,到处运行 自动内存管理,垃 ...
- CMS垃圾回收机制
详解CMS垃圾回收机制 原创不易,未经允许,不得转载~~~ 什么是CMS? Concurrent Mark Sweep. 看名字就知道,CMS是一款并发.使用标记-清除算法的gc. CMS是针对老 ...
- 图解 CMS 垃圾回收机制原理,-阿里面试题
最近在整理JVM相关的PPT,把CMS算法又过了一遍,每次阅读源码都能多了解一点,继续坚持. 什么是CMS CMS全称 ConcurrentMarkSweep,是一款并发的.使用标记-清除算法的垃圾回 ...
- 图解 CMS 垃圾回收机制,你值得拥有(转 强烈推荐)
首页 所有文章 资讯 Web 架构 基础技术 书籍 教程 Java小组 工具资源 - 导航条 - 首页 所有文章 资讯 Web 架构 基础技术 书籍 教程 Java小组 工具资源 ...
- 【译】Java SE 14 Hotspot 虚拟机垃圾回收调优指南
原文链接:HotSpot Virtual Machine Garbage Collection Tuning Guide,基于Java SE 14. 本文主要包括以下内容: 优化目标与策略(Ergon ...
- CMS 垃圾回收日志
CMS 垃圾回收日志 https://blogs.oracle.com/poonam/entry/understanding_cms_gc_logs http://www.blogjava.net/D ...
- Java虚拟机学习笔记——JVM垃圾回收机制
Java虚拟机学习笔记——JVM垃圾回收机制 Java垃圾回收基于虚拟机的自动内存管理机制,我们不需要为每一个对象进行释放内存,不容易发生内存泄漏和内存溢出问题. 但是自动内存管理机制不是万能药,我们 ...
随机推荐
- Spring mvc 参数半丁
http://blog.csdn.net/eson_15/article/details/51718633 众所周知,springmvc是用来处理页面的一些请求,然后将数据再通过视图返回给用户的,前面 ...
- dotnet跨平台 - 使用Nginx+Docker Compose运行.NETCore项目
参考文档: https://docs.docker.com/install/linux/docker-ce/centos/ http://www.dockerinfo.net/document htt ...
- [转载]边框回归(Bounding Box Regression)
[转载]边框回归(Bounding Box Regression) 许多模型中都应用到了这种方法来调整piror使其和ground truth尽量接近,例如之前自己看过的SSD模型 这篇文章写的很好, ...
- go语言入门(9)文本文件处理
1,字符串处理 字符串在开发中经常用到,包括用户的输入,数据库读取的数据等,我们经常需要对字符串进行分割.连接.转换等操作,我们可以通过Go标准库中的strings和strconv两个包中的函数进行相 ...
- Yii 2.0 GII 访问404错误
网上大部分都是普通的开启和配置资料 按照网上资料配置 访问localhost/index/php?r=gii 总是提示404错误 解决方法如下: Yii基础版中的 web.php 代码如下 if (Y ...
- weui 注意事项
个人随笔日记,还请小心采纳为甚 手机移动web开发必须要做的两点: 1.body中加上ontouchstart,即<body ontouchstart>...</body>: ...
- shell脚本——作业二(循环作业)
1.通过位置变量创建linux系统账户及密码 $1 是执行脚本的第一个参数,$2 是执行脚本的第二个参数 #!/bin/bash #创建用户与密码 declare -i c=0 if [ -z $1 ...
- 【3】Git命令
个人推荐的Git知识学习网站:https://git-scm.com . git常用操作图 init -> add -> commit -> remote -> push 初始 ...
- linux中公钥和私钥的区别以及关系
导读 在学习ssh章节时,一定有不少人对公钥和私钥产生过不解.在搜索公钥跟私钥的理解时,发现了这篇有趣的图解小文章,与大家共享. 1. 鲍勃有两把钥匙,一把是公钥,另一把是私钥. 2. 鲍勃把公钥送给 ...
- C/C++代码规范
零.前言 笔者最近在看开源代码,看到代码格式各自参差不齐,感觉像是各家各有所长.因此打算写一篇关于C/C++代码规范文章,请各位参考,并践踏批评. 一.文件排版 1. 包含头文件 • 先系统头文件,后 ...