大家好,我是树哥。

之前我们聊过 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. php 正则获取字符串中的汉字(去除字符串中除汉字外的所有字符)

    preg_match_all('/[\x{4e00}-\x{9fff}]+/u', $list[$i]['iparr'], $matches); $list[$i]['iparr'] = join(' ...

  2. 轻量级多级菜单控制框架程序(C语言)

    1.前言 作为嵌入式软件开发,可能经常会使用命令行或者显示屏等设备实现人机交互的功能,功能中通常情况都包含 UI 菜单设计:很多开发人员都会有自己的菜单框架模块,防止重复造轮子,网上有很多这种菜单框架 ...

  3. SAP 实例 10 List Box with value list from input help

    *&---------------------------------------------------------------------* *& Report DEMO_DROP ...

  4. 基于slate构建文档编辑器

    基于slate构建文档编辑器 slate.js是一个完全可定制的框架,用于构建富文本编辑器,在这里我们使用slate.js构建专注于文档编辑的富文本编辑器. 描述 Github | Editor DE ...

  5. python爬虫之protobuf协议介绍

    前言 在你学习爬虫的知识过程中是否遇到下面的类型.如果有兴趣学习一下或者了解相关知识的,且不嫌在下才疏学浅,可以参考一下.欢迎各位网友的指正. 首先叙述一下问题的会出现的式样. 你可能会在请求参数中看 ...

  6. linux目录结构及定时任务

    1. Linux的根目录(最顶层的目录) windows系统有根目录:c盘的根目录就是c:\ d盘的根目录就是d:\ 每个盘(分区)都有自己的根目录 Linux系统, 也支持多个分区 Linux的分区 ...

  7. SpringCloudAlibaba分布式流量控制组件Sentinel实战与源码分析-中

    实战示例 控制台初体验 Sentinel的控制台启动后,控制台页面的内容数据都是空的,接下来我们来逐步操作演示结合控制台的使用,在上一节也已说明整合SpringCloud Alibaba第一步先加入s ...

  8. NC14585 大吉大利,今晚吃鸡

    NC14585 大吉大利,今晚吃鸡 题目 题目描述 糖和抖m在玩个游戏,规定谁输了就要请谁吃顿大餐:抖m给糖a b c三个驻, 并在a柱上放置了数量为n的圆盘,圆盘的大小从上到下依次增大,现在要做的事 ...

  9. Linux操作系统(4):磁盘分区、挂载

    Outline: ① lsblk :查看所有设备挂载情况 ② df -h :查询系统整体磁盘使用情况 ③ du -h /目录 :查询指定目录的磁盘占用情况 ④ mount :查询系统中已经挂载的设备 ...

  10. Pytorch分布式训练

    用单机单卡训练模型的时代已经过去,单机多卡已经成为主流配置.如何最大化发挥多卡的作用呢?本文介绍Pytorch中的DistributedDataParallel方法. 1. DataParallel ...