前言

最近王子因为个人原因有些忙碌,导致文章更新比较慢,希望大家理解,之后也会持续和小伙伴们一起共同分享技术干货。

上篇JVM的文章中我们对ParNew和CMS垃圾回收器已经有了一个比较透彻的认识,感兴趣的小伙伴可以去回看一下探索ParNew和CMS垃圾回收器

今天我们继续探索垃圾回收器G1的原理,让我们开始吧!

G1的内存模型

G1是从jdk7开始出现的,在jdk9中被设为默认垃圾收集器,目标就是彻底替换掉CMS,那么为什么它可以替换掉CMS呢?

首先我们就来看看它的内存模型吧。

其实G1是可以同时回收年轻代和老年代的,他最大的特点就是把jvm堆内存拆分为了多个大小相等的Region,那么还存在年轻代和老年代吗?

答案是肯定的,不同的是新生代可能包含了某些Region,老年代也可能包含了某些Region,如下图:

那么到底有多少Region?每个Region有多大呢?

其实这个默认情况下是自动计算的,假如我们给定整个堆内存大小为4096M,然后使用“-XX:+UseG1GC”指定垃圾回收器为G1,此时会自动用堆内存大小除以2048,因为JVM最多可以有2048个Region,然后Region的大小必须是2的倍数。

堆内存为4096M,就会分配给每个Region 2M的内存空间。我们使用G1默认的计算方式就可以了。

当然也可以通过参数“-XX:G1HeapRegionSize”来指定Region的大小。

那么新生代和老年代的默认比例是多少呢?

我们知道使用ParNew和CMS垃圾回收器时,新生代和老年代的默认比例是1:2,而使用G1后,默认新生代对堆内存的初始占比是5%,这个可以通过“-XX:G1NewSizePercent”来设置初始占比,一般不需要设置。

细心的小伙伴会发现,这里说的占比是初始占比,因为系统运行的时候,JVM其实会不停的给新生代增加更多的Region,但是最多新生代的占比不会超过60%,可以通过“-XX:G1MaxNewSizePercent”来设置。

而一旦发生了垃圾回收,新生代的Region数量还会减少,所以其实新生代和老年代的占比不是一成不变的,而是动态改变的。

新生代还有eden和survivor吗?

答案是肯定的,新生代还是有eden和survivor的,只不过内存占用会随着Region的增多而增大。

G1的停顿时间控制

除了内存的变化,G1还有一个最大的变化,就是可以让我们设置一个垃圾回收的预期停顿时间,也就是说我们可以指定G1垃圾回收导致“Stop the World”的最长时间。

我们知道JVM一大痛点就是"Stop the World",尽量减少它的时间就可以做到JVM的优化。

引入G1后,我们可以自己去设定这个停顿的最长时间了,相当于直接控制了垃圾回收的性能。

G1要做到这一点就要去追踪每个Region的回收价值,那什么是回收价值呢?大家看下图:

比如两个Region中,其中一个有10M的垃圾对象,垃圾回收需要耗时1s,另一个有20M的垃圾对象,垃圾回收耗时200ms。

然后G1进行垃圾回收的时候,发现最近1小时垃圾回收已经导致了几百毫秒的系统停顿了,所以会选择回收价值高的Region进行回收,200ms的时间就能回收掉20M的垃圾对象,回收价值相对较高,所以会选择这个Region进行回收

G1控制停顿时间的思路,简单来讲就是,它会通过跟踪Region的回收价值,尽可能的保证系统停顿时间在你设定的停顿时间范围内。

G1的垃圾回收详解

上文我们了解到新生代还是有eden和survivor的,那么随着新生代占据堆内存大小的60%的时候,这个时候就会触发新生代的GC,G1也会使用之前我们说过的复制算法进行垃圾回收,进入一个“Stop the World”状态。

但是这个过程与之前的Minor GC其实是有差别的,首先回收的对象变成了带有垃圾对象的Region,然后回收的同时会根据设定的停顿时间进行价值回收,如上文所述。

那么什么时候进入老年代呢?

这个可以说和之前是一模一样的,简单介绍如下:

新生代躲过多次垃圾回收后会进入老年代;

GC后存活对象超过Survivor区的50%,那么会触发动态年龄判定规则,符合规则的进入老年代。

具体细节不在说明,可以参考王子之前的文章秒懂JVM的垃圾回收机制,有详细解释。

需要注意的是,G1的大对象不是存到老年代中的,而是提供了专门的Region来存放大对象。

在G1中,大对象的判断规则就是这个对象超过了一个Region大小的50%,比如Region是2M的,那如果你的对象超过了1M,就会被认定为大对象,做特殊处理。

而且如果这个大对象过大,可以横跨多个Region进行存储,如下图:

那么老年代具体又是怎么进行垃圾回收的呢

这个过程说起来可能稍微复杂了一点,但是它和CMS的垃圾回收过程其实是类似的。关于CMS的垃圾回收的几个阶段可以回顾王子的上篇文章探索ParNew和CMS垃圾回收器

首先我们要弄明白,什么时候会触发新生代和老年代的混合垃圾回收?

G1有一个参数“-XX:InitiatingHeapOccupancyPercent”,默认值为45%。

什么意思呢?就是说当老年代占据了堆内存的45%的Regionf的时候,就会触发混合垃圾回收。

那具体流程是什么样的呢?

首先会触发一次初始标记操作,这个过程是要“Stop the World”的,对应的就是CMS的初始标记阶段,细节不再说明。

接着会进入并发标记阶段,这个阶段同样对应CMS的并发标记阶段,不再说明。

接着会进入最终标记阶段,这个阶段其实和CMS的重新标记阶段也基本一致。

最后就是混合回收阶段,这个阶段和CMS的并发清理阶段就不太一样了,这个阶段会计算每个Region中的存活对象数量,存活数量占比,还有执行垃圾回收的耗时等问题。

接着会进入“Stop the World”阶段,然后全力以赴进行垃圾回收,并尽量保证停止时间不超过我们设定好的时间,所以可能只会回收掉之前标记好的一部分垃圾对象。

为什么要叫做混合回收呢,因为它不仅仅回收的是老年代,新生代和大对象的Region也会同时进行回收,而具体回收哪些Region就要视情况而定了,根据价值回收价值G1会自己做出选择。

而混合回收是可以进行多次的,比如先停止系统,混合回收掉一部分Region,再停止系统,再执行一次混合回收。

有参数可以控制这个数量,“-XX:G1MixedGCCountTarget”参数,就是在一次混合回收的过程中,最后一个阶段执行几次,默认是8次。

那么为什么要这样反复多次的回收呢?

因为这样每次回收停止系统的时间都很短,在回收的间隙系统是可以正常运行的。

还有个参数“-XX:G1HeapWastePercent”,默认值是5%。

它的意思是,混合回收的时候,都是基于复制算法进行的,把Region存活的对象放入其他Region,然后清除掉本来的Region。那么当空闲的Region数量达到堆内存的5%,就会立即停止混合回收。

而通过这种复制算法回收,也不会出现像CMS标记清理算法导致的内存碎片问题。

还有个参数“-XX:G1MixedGCLiveThresholdPercent”,默认值是85%,意思就是回收Region的时候,存活的对象必须少于85%才可以被回收掉。否则存活对象太多,复制的时候成本是很高的。

那么如果回收失败怎么办?

如果在复制的时候发现没有空闲的Region可以承载存活的对象,那么会触发失败,立马停止系统进程,采用单线程进行标记、清理和压缩整理,空闲出一批Region,这个过程是极慢的。

总结

本文我们对G1的内存机制和垃圾回收的算法做了一个比较清晰的解释。

阅读完本文,相信小伙伴们自己可以总结出G1和CMS究竟有什么不一样了吧。

欢迎小伙伴们留言区讨论G1和CMS的区别,王子会第一时间回复。

那我们下篇文章再见。

往期文章推荐:

大白话谈JVM的类加载机制

JVM内存模型不再是秘密

轻松理解JVM的分代模型

秒懂JVM的垃圾回收机制

探索ParNew和CMS垃圾回收器

探索G1垃圾回收器的更多相关文章

  1. G1垃圾回收器

    垃圾回收器的发展历程 背景 01.G1解决的问题 G1垃圾回收器是04年正式提出,12开始正式支持,在17年作为JDK9默认的垃圾处理器. 在04年的时候,java程序堆的内存越来越大,从而导致程序中 ...

  2. 深入浅出具有划时代意义的G1垃圾回收器

    G1诞生的背景 Garbage First(简称G1)收集器是垃圾收集器技术发展历史上的里程碑式的成果,它开创了收集器面向局部收集的设计思路和基于Region的内存布局形式.HotSpot开发团队最初 ...

  3. JVM学习——G1垃圾回收器(学习过程)

    JVM学习--G1垃圾回收器 把这个跨时代的垃圾回收器的笔记独立出来. 新生代:适用复制算法 老年代:适用标记清除.标记整理算法 二娃本来看G1的时候觉得比较枯燥,但是后来总结完之后告诉我说,一定要慢 ...

  4. G1垃圾回收器在并发场景调优

    一.序言 目前企业级主流使用的Java版本是8,垃圾回收器支持手动修改为G1,G1垃圾回收器是Java 11的默认设置,因此G1垃圾回收器可以用很长时间,现阶段垃圾回收器优化意味着针对G1垃圾回收器优 ...

  5. G1 垃圾回收器简单调优

    G1: Garbage First 低延迟.服务侧分代垃圾回收器. 详细介绍参见:JVM之G1收集器,这里不再赘述. 关于调优目标:延迟.吞吐量 一.延迟,单次的延迟 单次的延迟关系到服务的响应时延, ...

  6. JAVA之G1垃圾回收器

    概述 G1 GC,全称Garbage-First Garbage Collector,通过-XX:+UseG1GC参数来启用,作为体验版随着JDK 6u14版本面世,在JDK 7u4版本发行时被正式推 ...

  7. jvm默认的并行垃圾回收器和G1垃圾回收器性能对比

    http://www.importnew.com/13827.html 参数如下: JAVA_OPTS="-server -Xms1024m -Xmx1024m -Xss256k -XX:M ...

  8. G1垃圾回收器参数配置

    下面是完整的 G1 的 GC 开关参数列表. 选项/默认值 说明 -XX:+UseG1GC 使用 G1 (Garbage First) 垃圾收集器 -XX:MaxGCPauseMillis=n 设置最 ...

  9. 探索ParNew和CMS垃圾回收器

    前言 上篇文章我们一起分析了JVM的垃圾回收机制,了解了新生代的内存模型,老年代的空间分配担保原则,并简单的介绍了几种垃圾回收器.详细内容小伙伴们可以去看一下我的上篇文章:秒懂JVM的垃圾回收机制. ...

随机推荐

  1. C# 中的 is 真的是越来越强大,越来越语义化

    一:背景 1. 讲故事 最近发现 C#7 之后的 is 是越来越看不懂了,乍一看花里胡哨的,不过当我静下心来仔细研读,发现这 is 是越来越短小精悍,而且还特别语义化,那怎是一个爽字了得,这一篇就和大 ...

  2. Centos-当前登录用户信息- w who

    w who 显示当前登录系统的用户,但w显示的更为详细 who 相关参数 # 默认输出 用户名.登录终端.登录时间 -a 列出所有信息 -b    系统最近启动日期 -m   当前终端信息,相当于 w ...

  3. try-finally的时候try里面带return

    最近学习的JVM小册中老师提了个问题: 最开始我觉得是1,结果程序跑出来是0,感到很疑惑,于是查看了下字节码: 从字节码可以看出: 0:定义变量0 1:将0存入本地变量表slot-0 2:加载slot ...

  4. 草率了,又一个Maven打包的问题

    经常遇到 Maven 相关的问题,这是之前的文章: 这个 Maven 依赖的问题,你敢说你没遇到过:https://mp.weixin.qq.com/s/SzBbDtyRUrk_7LH8SUbGXQ ...

  5. ConcurrentHashMap原理分析(二)-扩容

    概述 在上一篇文章中介绍了ConcurrentHashMap的存储结构,以及put和get方法,那本篇文章就介绍一下其扩容原理.其实说到扩容,无非就是新建一个数组,然后把旧的数组中的数据拷贝到新的数组 ...

  6. 在实际开发中Java中enum的用法

    在日常项目的开发中,往往会存在一些固定的值,而且"数据集"中的元素是有限的. 例如:st_code// 一些状态机制:01-激活  02-未激活  03 -注册..等等 还有一特性 ...

  7. 持续集成工具之jenkins+sonarqube做代码扫描

    上一篇我们主要聊了下代码质量管理平台sonarqube的安装部署以及它的工作方式做了简单的描述和代码扫描演示:回顾请参考https://www.cnblogs.com/qiuhom-1874/p/13 ...

  8. scp带密码拷贝文件

    应用场景:将B服务器的文件传输到A服务器.核心命令: sshpass -p 123456 scp ubuntu@192.168.52.1:/home/ubuntu/"TEST"'' ...

  9. centos8平台使用mpstat监控cpu

    一,mpstat的用途 mpstat是 Multiprocessor Statistics的缩写,是实时cpu监控工具. 在多CPU系统里,其不但能查看所有CPU的平均状况信息,而且能够查看特定CPU ...

  10. mac 解决安卓模拟器链接不上网络

    方法1.临时方法,每次启动都要加114.114.114.114 1.进入到下面的目录 /Users/anxiaodong/Library/Android/sdk/emulator 2.执行以下命令 e ...