一、何为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. 【Oracle】常见等待事件处理

    1.查看数据库中需要关注的等待事件: select sw.seq#,sw.sid||','||s.serial# sids,s.username,sw.event,sw.P1,sw.p2,sw.p3, ...

  2. 【Oracle】增量备份和全库备份怎么恢复数据库

    1差异增量实验示例 1.1差异增量备份 为了演示增量备份的效果,我们在执行一次0级别的备份后,对数据库进行一些改变. 再执行一次1级别的差异增量备份: 执行完1级别的备份后再次对数据库进行更改: 再执 ...

  3. 以事实驳斥:改进你的c#代码的5个技巧(四)

    测试使用的环境:vs2019+.net core3.1 原文地址:https://www.cnblogs.com/hhhnicvscs/p/14296715.html 反驳第一条:如何检查代码中的空字 ...

  4. 大文件上传FTP

    需求 将本地大文件通过浏览器上传到FTP服务器. 原有方法 将本地文件整个上传到浏览器,然后发送到node服务器,最后由node发送到FTP服务器. 存在问题 浏览器缓存有限且上传速率受网速影响,当文 ...

  5. LeetCode349. 两个数组的交集

    题目 给定两个数组,编写一个函数来计算它们的交集. 分析 数组元素值可以很大,所以不适合直接开数组进行哈希,这里要学习另一种哈希方式:集合 集合有三种,区别见下面代码随想录的Carl大佬的表格,总结的 ...

  6. SSTI

    最牛bypass:https://blog.csdn.net/solitudi/article/details/107752717 SSTI的奇怪绕过姿势:https://blog.csdn.net/ ...

  7. 在Firefox上使用Chrome的crx扩展程序

    假如你喜欢使用Firefox火狐浏览器,可是发现有个很喜欢很想用的扩展只发布了支持Chrome的crx格式--Firefox从57版以后使用了WebExtension API作为新附加组件的开发标准, ...

  8. 发票校验BAPI_INCOMINGINVOICE_CREATE

    CALL FUNCTION 'BAPI_INCOMINGINVOICE_CREATE'    EXPORTING      headerdata                = headerdata ...

  9. Python爬虫学习笔记(一)

    概念: 使用代码模拟用户,批量发送网络请求,批量获取数据. 分类: 通用爬虫: 通用爬虫是搜索引擎(Baidu.Google.Yahoo等)"抓取系统"的重要组成部分. 主要目的是 ...

  10. day131:2RenMJ:2RenMJ游戏简介&部署MJ项目到本地

    目录 1.游戏简介 1.如何做出一款麻将游戏? 2.麻将运行界面 3.麻将项目所用技术快速概览 4.web开发 / 游戏开发 / APP开发 比较 5.firefly游戏框架介绍 2.部署麻将项目到本 ...