大家好,我是树哥。

之前我们聊过 CMS 回收器,但那时候我们说 CMS 回收器已经落伍了,现在应该是用 G1 回收器的时候了。那么 G1 回收器到底有什么魔力,它比 CMS 回收器相比强在哪里呢?今天,就让树哥带大家盘一盘!

G1 回收器的历史

G1(Garbage-First)回收器早在 JDK1.7 的时候就确定要做,但直到 JDK7u4 的时候才正式推出使用。等到了 JDK9 之后变成了默认的垃圾回收器,同时废弃了 CMS 回收器。

G1 回收器特性

G1 回收器是一款面向服务端应用的垃圾回收器,它的长期实名是替换 CMS 回收器。

G1 回收器于 CMS 回收器相比,它们有相似的地方,例如:都是关注 GC 停顿时间的回收器,都采用了分代回收的思想。但从整体的实现上来看,G1 回收器做了非常多的改进,可以说是对 CMS 回收器的全面改进。相对于 CMS 回收器来说,G1 回收器有下面几个不同的地方:

  1. 采用化整为零的分区思想
  2. 采用标记-整理的垃圾回收算法
  3. 可预测的 GC 停顿时间

分区思想

对于 CMS 及之前的回收器来说,其 JVM 内存空间按照分代的思路划分成物理连续的一大片区域,如下图所示。

但在 G1 回收器中,虽然也采用了分代的思路,但其并没有为其分配一块连续的内存,而是将整块内存化整为零拆分成一个个 Region,如下图所示。

正如上图所示,G1 回收器不再为年轻代和老年代划分大块的内存,而是划分成了一个个的 Region,每个 Region 被标记成年轻代或者老年代。在 G1 中,还多了一个 Humongous 区域,其是为了优化大对象的分配而诞生的。

G1 回收器化整为零的 Region 设计思想,是 G1 回收器比 CMS 回收器强大的核心。 通过将大块的内存化整为零,G1 回收器能够更加灵活地控制 GC 停顿时间,并且也解决了 CMS 回收器存在的内存碎片问题以及大内存下的长 GC 停顿时间问题。

标记-整理算法

G1 回收器与 CMS 回收器的另一个很大的区别是:G1 回收器使用的是「标记-整理」算法,而 CMS 回收器使用的是「标记-清除」算法。 因此,CMS 回收器会产生非常多的内存碎片,而 G1 回收器则没有这个困扰。

有些小伙伴会问:那为什么 CMS 回收器不用「标记-整理」算法呢?

很简单,因为 CMS 回收器的老年代很大,使用「标记-整理」算法需要耗费很长的 GC 停顿时间,这会导致接口响应时间变长。实际上 CMS 回收器后续提供了 -XX:+UseCMSCompactAtFullCollection 参数去实现内存压缩,但在内存压缩的时候 GC 停顿时间会很长,从而导致接口响应时间变长。

好奇宝宝又问了:G1 回收器也用的是「标记-整理」算法,为啥就不会导致长 GC 停顿时间呢?

很简单,因为 G1 回收器使用了分 Region 的思想,其将大块的内存化整为零成为 Region。此外,其还维护了一个待回收 Region 列表,可以选择回收性价比最高的 Region 进行回收,从而实现对 GC 停顿时间的灵活控制。

看到了么,G1 回收器化整为零的 Region 设计思想,真的是 G1 回收器的大杀器!

可预测的停顿时间

G1 回收器对于 CMS 而言还有一个很大的优势,即能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为 M 毫秒的时间片段内,消耗在垃圾收集上的时间不得超过 N 毫秒。对于该特性现在还用得比较少,大家了解一下就可以了。

垃圾回收过程

比起 CMS 回收器来说,G1 回收器的垃圾回收过程就比较特别了,其采用了「年轻代收集」和「混合收集」两种垃圾回收方式。

年轻代收集

在应用刚刚启动的时候,流量慢慢进来,JVM 开始生成对象。G1 会选择一个分区并指定 eden 分区,当这块分区用满之后,G1 会选一个新的分区作为 eden 分区。这个操作会一直进行下去,一直到达到 eden 分区的上限,接着触发一次年轻代收集。

年代收集采用的是「复制算法」,其首先使用单 eden、双 survivor 迁移存活对象。在迁移过程中,会根据对象年龄以及其他特性,将对象晋升到老年代分区中,原有的年轻代分区会被整个回收掉。这个过程涉及到的规则和 CMS 回收器类似,只是 G1 回收器将内存化整为零了而已。

混合收集

随着时间推移,越来越多的对象晋升到老年代中。当老年代占比(占 Java 堆内存的比例)达到 InitiatingHeapOccupancyPercent 参数之后,JVM 便会触发「混合收集」进行垃圾收集。要注意的是:混合收集会收集年轻代和部分老年代的内存,其并不等同于 Full GC。Full GC 会回收整个老年代内存。

对于混合收集方式来说,其收集过程可以分为 4 个阶段:

  • 初始标记
  • 并发标记
  • 最终标记
  • 筛选回收

初始标记。 该阶段与 CMS 回收器一样,都只是简单标记一下 GC Roots 能直接关联到的对象,让后续 GC 回收线程能与用户线程并发执行。初始标记阶段是需要「Stop the World」的。

并发标记。 该阶段与 CMS 回收器一样,它从 GC Root 开始对堆中对象进行可达性分析,找出存活的对象,这阶段耗时很长,但可与用户程序并发执行,不需要「Stop the World」。

最终标记。 该阶段与 CMS 回收器一样,它是为了修正在并发标记期间因用户程序继续运作而导致引用发生变化的问题。只是 G1 回收器采用了不同的方式去实现,在这个阶段是需要「Stop the World」的。

筛选回收。 该阶段与 CMS 回收器的并发清除一样,它是去将标记为垃圾的对象清除掉。只是对于 G1 回收器来说,它会维护各个 Region 的回收价值和成本,随后根据用户期望的 GC 停顿时间来指定回收计划。

整体看下来,我们会发现 G1 回收器的混合收集过程与 CMS 回收器非常类似,都经历初始标记、并发标记、最终标记、筛选回收(并发清除)几个阶段。

总结

从 JDK7 正式推出到 JDK9 成为默认的垃圾收集器,G1 回收器用了两代人的时间打败了 CMS 回收器。

从 G1 回收器的实现来看,其开创性的化整为零的 Region 设计思想,无疑是其打败 CMS 回收器的秘诀。通过该设计思想,G1 回收器得以更加灵活地控制 GC 停顿时间,同时也可以实现更加高效、复杂的功能,例如:根据回收空间和耗时选择最佳的回收 Region、预测 GC 停顿时间等。

参考资料

这三大特性,让 G1 取代了 CMS!的更多相关文章

  1. 前端基础-CSS的各种选择器的特点以及CSS的三大特性

    一. 基本选择器 二. 后代选择器.子元素选择器 三. 兄弟选择器 四. 交集选择器与并集选择器 五. 序列选择器 六. 属性选择器 七. 伪类选择器 八. 伪元素选择器 九. CSS三大特性 一. ...

  2. day20面向对象三大特性 , 类嵌套

    #!/usr/bin/env python# -*- coding:utf-8 -*- # 1.简述面向对象三大特性并用代码表示."""封装:class Account: ...

  3. C++ 三大特性:封装、继承、多态性

    要讲  封装.继承.多态就必须从面向对象说起 开发一个软件是为了解决某些问题,这些问题所涉及的业务范围称为该软件的问题域.面向对象的编程语言将客观事物看作具有属性和行为(或服务)的对象,通过抽象找出同 ...

  4. Java面向对象概述和三大特性

    Java 是面向对象的高级编程语言,类和对象是 Java 程序的构成核心.围绕着 Java 类和 Java 对象,有三大基本特性:封装是 Java 类的编写规范.继承是类与类之间联系的一种形式.而多态 ...

  5. Java面向基础概述和三大特性

    Java 是面向对象的高级编程语言,类和对象是 Java 程序的构成核心.围绕着 Java 类和 Java 对象,有三大基本特性:封装是 Java 类的编写规范.继承是类与类之间联系的一种形式.而多态 ...

  6. Fis3的前端工程化之路[三大特性篇之声明依赖]

    Fis3版本:v3.4.22 Fis3的三大特性 资源定位:获取任何开发中所使用资源的线上路径 内容嵌入:把一个文件的内容(文本)或者base64编码(图片)嵌入到另一个文件中 依赖声明:在一个文本文 ...

  7. Fis3的前端工程化之路[三大特性篇之资源定位]

    Fis3版本:v3.4.22 Fis3的三大特性 资源定位:获取任何开发中所使用资源的线上路径 内容嵌入:把一个文件的内容(文本)或者base64编码(图片)嵌入到另一个文件中 依赖声明:在一个文本文 ...

  8. Fis3的前端工程化之路[三大特性篇之内容嵌入]

    Fis3版本:v3.4.22 Fis3的三大特性 资源定位:获取任何开发中所使用资源的线上路径 内容嵌入:把一个文件的内容(文本)或者base64编码(图片)嵌入到另一个文件中 依赖声明:在一个文本文 ...

  9. JS三大特性

    抽象 在分析三大特性之前我们要先了解什么叫抽象. 定义: 在定义一个类的时候,实际上就是把一类事物的共有的属性和行为提取出来,形成一个物理模型(模板),这种研究问题的方法就称为抽象 一.封装 定义: ...

随机推荐

  1. 【小程序自动化Minium】一、框架介绍和环境搭建

    微信小程序自动化测试系列分享 一.Minium 简介 minium 是微信团队为小程序专门开发的自动化框架,我们可以用它来做小程序的UI自动化测试,但是它的能力却不仅仅在于UI自动化. 正是得益于官方 ...

  2. Redis的内存淘汰策略(八)

    一:Redis的AOF是什么? 以日志的形式来记录每个写操作(读操作不记录),将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构 ...

  3. Vue回炉重造之三次封装axios

    源码目录 在src目录下建立一个request文件夹.里面建立两个文件: http.js api.js 源码内容 http.js import axios from 'axios' // 引入axio ...

  4. orcal恢复delete误删除的数据

    orcal的删除有3种:delete.truncate.drop. delete可以手动提交和回滚,且可以使用where:而truncate.drop执行即对表数据进行了修改,且不能使用where. ...

  5. npm相关知识整理

    语义化版本 major: 重大变化,不兼容老版本 minor: 新增功能,兼容老版本 patch: 修复bug,兼容老版本 依赖版本号 * 匹配最新版本的依赖 ^ 匹配最近的大版本依赖,比如^1.2. ...

  6. 安卓fastboot刷机、刷magisk、aidlux备忘

    环境就不多说了,网上一堆教程,我只在这边简单记录一下,以小米手机为例 刷机 解锁bootloader PC上配置好adb.fastboot,也就是platform-tools工具包加入系统变量,在命令 ...

  7. C++类中的常成员和静态成员

    常变量.常对象.常引用.指向常对象或常变量的指针等在定义时都使用了const关键字,这是C++语言引入的一种数据保护机制,称为const数据保护机制.例如通过const关键字主动地将被调函数形参进行限 ...

  8. 使用OnPush和immutable.js来提升angular的性能

    angular里面变化检测是非常频繁的发生的,如果你像下面这样写代码 <div> {{hello()}} </div> 则每次变化检测都会执行hello函数,如果hello函数 ...

  9. PHP正则替换函数收集

    preg_replace() $msg = preg_replace("/<style>.+<\/style>/is", "", $ms ...

  10. 在阿里云Centos7.6上利用docker搭建Jenkins来自动化部署Django项目

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_84 一般情况下,将一个项目部署到生产环境的流程如下: 需求分析-原型设计-开发代码-内网部署-提交测试-确认上线-备份数据-外网更 ...