一、何为G1收集器

The Garbage-First (G1) garbage collector is a server-style garbage collector, targeted for multiprocessor machines with large memories. It attempts to meet garbage collection (GC) pause time goals with high probability while achieving high throughput. Whole-heap operations, such as global marking, are performed concurrently with the application threads. This prevents interruptions proportional to heap or live-data size.

Garbage First(简称G1)收集器是垃圾收集器技术发展史上里程碑式的成果,它开创了收集器面向局部收集的设计思路和基于Region的内存布局形式。

作为CMS收集器的替代者和继承人,设计者们希望做出一款能够建立起“停顿时间模型”的收集器,停顿时间模型的意思是能够支持指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间大概率不超过N秒这样的目标。

二、G1收集器内存管理

1、Mixed GC模式

G1的里程碑意义来源于其面向局部收集的设计思路和基于Region的内存布局形式,这也是G1实现其停顿时间模型的底气。在G1收集器出现之前的所有其他收集器,包括被它所替代的CMS,垃圾收集的目标范围都是整个新生代(Minor GC)或整个老年代(Major GC),亦或者是整个Java堆(Full GC)。而G1实现了可以面向堆内存的任何部分来组成回收集。衡量标准不再是分代,而是回收的实际收益,这就是Mixed GC模式。

2、基于Region的堆内存布局

G1基于Region的堆内存布局是它实现Mixed GC的关键。我们不能说G1摈弃了分代理论,相反,G1依然是依据分代理论设计的,但其堆内存布局与其他收集器有非常明显的差异,它不再坚持固定大小以及固定数量的分代区域划分,而是把堆分成多个大小相等的独立区域,称为Region,而每个Region都可能是新生代或老年代。这样无论是针对哪种对象,都可以有比较好的收集效果。



从上图中我们可以看见,Region中还有一种Humongous区域,它专门用来存储大对象。G1认为一个对象的大小超过了一个Region的一半,那就可以称为大对象。如果对象大小超过一个Region,就存储在连续的多个Region当中。另外值得注意的是,G1的大部分行为都把Humongous Region作为老年代的一部分来看待。

3、G1具有优先级的区域回收方式

G1之所以能够建立起可预测的停顿时间模型,是因为它将Region作为单次回收的最小单元。G1收集器会跟踪每个Region中垃圾总的“价值”大小,即回收所获得的空间大小和回收所需时间的经验值,然后在后台维护一个优先级列表。并可以根据用户的设定回收价值收益最大的Region。这也是“Garbage First”其名的由来。

三、G1收集器开发者花大量时间解决的三个尖锐问题

G1收集器作为一款跨时代的收集器,它从发表论文到商用经历了超过十年的研发,其中解决了无数的问题,以下是三个典型且重要的问题向读者说明。

1、跨Region引用问题如何解决?

和其他收集器解决跨代问题的方法一样,G1使用记忆集从而避免全堆作为GC Roots扫描。但不同的是G1的每个Region都维护属于自己的记忆集,它们会记录下别的Region指向自己的指针,并标记这些指针分别在哪些卡页的范围内。G1的记忆集在存储结构的本质上是哈希表(key是别的Region的起始地址,Value是卡表索引号的集合)。

由于Region的数量较多,而每个Region都有自己的记忆集,所以G1收集器要花费更大的内存来维持工作,这个数通常是Java堆的10%~20%。

2、在并发阶段如何保证收集线程与用户线程互不干扰地运行?

首先,用户线程改变对象引用关系时,必须保证不打破原本的对象图结构,导致标记结果出现错误。CMS对这个问题采取了增量更新的算法进行解决,而G1选择了原始快照(SATB)的方法进行解决。

其次,回收过程中会有新对象需要进行内存分配。G1为每个Region设置了两个名为TAMS的指针,把Region的一部分空间用于并发回收过程中的新对象分配。新对象的地址必须在这两个指针之上。这部分空间被收集器视为默认存货, 不纳入回收范围。

3、怎样建立起可靠的停顿预测模型?

G1收集器的停顿预测模型是以衰减均值作为理论基础来实现的。在垃圾收集过程中,G1收集器会记录每个Region的回收时间、记忆集中的脏卡数量等各个步骤的成本,并按照一定的统计信息和统计算法得出“衰减平均值”。衰减平均值更准确地代表了最近的平均状态,Region的统计状态越新就越能决定回收的价值。

根据这些信息,收集器可以决定应当找出哪些Region进入回收集,最终在不超过期望时间的前提下获得最高收益。

四、G1收集器实际运作的四大步骤

1、初始标记

标记一下GC Roots能直接关联到的对象。并且修改TAMS的值,让并发标记阶段分配对象有据可依。这个阶段需要停顿线程,但耗时很短,并且在Minor GC时同步完成。

2、并发标记

从GC Root中开始对堆中对象进行可达性分析,扫描对象图,找出要回收的对象,这阶段耗时长但是可以和用户程序并发执行。当对象图扫描完成后,还要重新处理SATB记录下的在并发时有引用变动的对象。

3、最终标记

对用户线程做一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的最后那些少量的SATB记录。

4、筛选回收

负责更新Region的统计数据,对各个Region的回收价值和成本进行排序,制定具体的回收计划。可以自由地选择多个Region作为回收集,把其中存活的对象复制到空的Region中,再清理掉整个回收集。由于涉及存活对象的移动,所以必须暂停用户线程。



总之,G1收集器的设计目标是在延迟可控的前提下获得尽可能高的吞吐量。

五、G1收集器与CMS收集器的比较

作为两款关注停顿时间的收集器,G1常被作为CMS收集器的比较对象。在今天,G1已经几乎完全取代了CMS的地位,但这并不意味着CMS在G1面前不值一提。

先说明一个事实:在小内存上CMS的表现可能会优于G1,而大内存上G1毫无疑问会占据优势。这个堆内存大小的平衡点通常在6~8GB左右。

1、G1的新设计带来的优势

指定最大停顿时间、分Region的内存布局、按收益确定最终回收集,这些都是G1的新设计给它带来的相对于CMS的优势。

2、整体收集算法的不同

CMS集于标记-清除算法进行收集,而G1从整体看集于标记-整理算法进行收集,局部看基于标记-复制算法进行收集。显然,G1的两种解读方法都意味着它不会产生任何内存碎片。这样的特性有利于程序长久地平稳运行。

3、内存消耗不同

我们前文中提到,G1收集器为每一个Region都提供了卡表作为记忆集,显然这意味着G1相比CMS需要消耗更大量的内存来完成其本职工作。相比之下CMS的卡表仅有一份且实现简单。

4、执行负载不同

CMS使用写后屏障来更新和维护卡表。G1除了使用写后屏障,为了实现快照搜索算法,它还得使用写前屏障来跟踪并发时的指针变化情况。这也引出了原始快照和增量更新两种方法的比较:原始快照能够减少并发标记和重新标记阶段的损耗,避免在标记阶段停顿时间过长,但它同时也会产生由于跟踪引用变化带来的额外负担。

由于G1写屏障的复杂操作要比CMS消耗更多的运算资源,CMS的写屏障实现是直接的同步操作,而G1必须把它实现为类似消息队列的架构,即把写前屏障和写后屏障要做的事都放到队列里,然后异步处理。

一篇文章搞懂G1收集器的更多相关文章

  1. 一篇文章搞懂Python装饰器所有用法

    01. 装饰器语法糖 如果你接触 Python 有一段时间了的话,想必你对 @ 符号一定不陌生了,没错 @ 符号就是装饰器的语法糖. 它放在一个函数开始定义的地方,它就像一顶帽子一样戴在这个函数的头上 ...

  2. 一篇文章搞懂高级程序员、架构师、技术总监、CTO从薪资到技能的区别

    一篇文章搞懂高级程序员.架构师.技术总监.CTO从薪资到技能的区别 http://youzhixueyuan.com/senior-programmers-architects-technical-d ...

  3. 一篇文章读懂Java类加载器

    Java类加载器算是一个老生常谈的问题,大多Java工程师也都对其中的知识点倒背如流,最近在看源码的时候发现有一些细节的地方理解还是比较模糊,正好写一篇文章梳理一下. 关于Java类加载器的知识,网上 ...

  4. 一篇文章搞懂python2、3编码

    说在前边: 编码问题一直困扰着每一个程序员的编程之路,如果不将它彻底搞清楚,那么你的的这条路一定会走的格外艰辛,尤其是针对使用python的程序员来说,这一问题更加显著, 因为python有两个版本, ...

  5. 搞懂G1垃圾收集器

    一.G1 GC术语Overview 1.1 并发 并发的意思是Java应用执行和垃圾收集活动可以同时进行 1.2 并行 并行的意思是垃圾收集运算是多线程执行的,比如CMS垃圾收集器的年轻代就是并行的, ...

  6. 一篇文章搞懂filebeat(ELK)

    本文使用的filebeat是7.7.0的版本本文从如下几个方面说明: filebeat是什么,可以用来干嘛 filebeat的原理是怎样的,怎么构成的 filebeat应该怎么玩 一.filebeat ...

  7. 五分钟学Java:一篇文章搞懂spring和springMVC

    原创声明 本文作者:黄小斜 转载请务必在文章开头注明出处和作者. 本文思维导图 什么是Spring,为什么你要学习spring? 你第一次接触spring框架是在什么时候?相信很多人和我一样,第一次了 ...

  8. Java多线程详解——一篇文章搞懂Java多线程

    目录 1. 基本概念 2. 线程的创建和启动 2.1. 多线程实现的原理 2.2.多线程的创建,方式一:继承于Thread类 2.3.多线程的创建,方式一:创建Thread匿名子类(也属于方法一) 2 ...

  9. 一篇文章搞懂Android组件化

    网上组件化的文章很多,我本人学习组建化的过程也借鉴了网上先辈们的文章.但大多数文章都从底层的细枝末节开始讲述,由下而上给人一种这门技术“博大精深”望而生畏的感觉.而我写这篇文章的初衷就是由上而下,希望 ...

随机推荐

  1. 【MySQL】ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing

    今天上午遇到了一个问题,新创建的mysql5.7的数据库,由于初始化有点问题,没有给root密码,用了免密码登录. 但是,修改了root密码之后,把配置中的免密登录的配置注释掉后,重启服务.服务正常启 ...

  2. linux多路径(multipath)

    https://www.itread01.com/articles/1475909423.html

  3. css全站变灰

    2020年4月4日全国哀悼日这一天,我发现不少网址都变灰了,我第一想法就是怎么做到的?不可能换素材整个网址重做一遍吧?后面发现是用的其实是css的filter滤镜: grayscale可以将图像转化为 ...

  4. Java多线程基础知识笔记(持续更新)

    多线程基础知识笔记 一.线程 1.基本概念 程序(program):是为完成特定任务.用某种语言编写的一组指令的集合.即指一段静态的代码,静态对象. 进程(process):是程序的一次执行过程,或是 ...

  5. Android 代码规范大全

    前言 虽然我们项目的代码时间并不长,也没经过太多人手,但代码的规范性依然堪忧,目前存在较多的比较自由的「代码规范」,这非常不利于项目的维护,代码可读性也不够高, 此外,客户端和后端的研发模式也完全不同 ...

  6. Ubuntu源、Python虚拟环境及pip源配置

    Ubuntu 命令行更改源 在修改source.list前,最好先备份一份 软件源的地址配置文件在 /etc/apt/sources.list 执行备份命令 sudo cp /etc/apt/sour ...

  7. 微信小程序腾讯地图SDK使用方法

    一.本篇文章主要知识点有以下几种: 1.授权当前位置 2.map组件的使用 3.腾讯地图逆地址解析 4.坐标系的转化 二.效果如下: 三.WXML代码 <map id="map&quo ...

  8. Kubernetes调整Node节点快速驱逐pod的时间

    在高可用的k8s集群中,当Node节点挂掉,kubelet无法提供工作的时候,pod将会自动调度到其他的节点上去,而调度到节点上的时间需要我们慎重考量,因为它决定了生产的稳定性.可靠性,更快的迁移可以 ...

  9. 数字化转型中企业真正困惑-传统IT架构如何改造和全面上云

    对数字化转型,整体来看大部分人相对关心问题主要还是集中在以下两个方面. 企业传统的IT架构如何如何微服务改造,演进发展 企业传统IT如何全面上云和实施云原生 以上两点实际都包括一个关键点,即企业当前内 ...

  10. nodejs的调试debug

    目录 简介 开启nodejs的调试 调试的安全性 使用WebStorm进行nodejs调试 使用Chrome devTools进行调试 使用node-inspect来进行调试 其他的debug客户端 ...