一篇文章搞懂G1收集器
一、何为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收集器的更多相关文章
- 一篇文章搞懂Python装饰器所有用法
		01. 装饰器语法糖 如果你接触 Python 有一段时间了的话,想必你对 @ 符号一定不陌生了,没错 @ 符号就是装饰器的语法糖. 它放在一个函数开始定义的地方,它就像一顶帽子一样戴在这个函数的头上 ... 
- 一篇文章搞懂高级程序员、架构师、技术总监、CTO从薪资到技能的区别
		一篇文章搞懂高级程序员.架构师.技术总监.CTO从薪资到技能的区别 http://youzhixueyuan.com/senior-programmers-architects-technical-d ... 
- 一篇文章读懂Java类加载器
		Java类加载器算是一个老生常谈的问题,大多Java工程师也都对其中的知识点倒背如流,最近在看源码的时候发现有一些细节的地方理解还是比较模糊,正好写一篇文章梳理一下. 关于Java类加载器的知识,网上 ... 
- 一篇文章搞懂python2、3编码
		说在前边: 编码问题一直困扰着每一个程序员的编程之路,如果不将它彻底搞清楚,那么你的的这条路一定会走的格外艰辛,尤其是针对使用python的程序员来说,这一问题更加显著, 因为python有两个版本, ... 
- 搞懂G1垃圾收集器
		一.G1 GC术语Overview 1.1 并发 并发的意思是Java应用执行和垃圾收集活动可以同时进行 1.2 并行 并行的意思是垃圾收集运算是多线程执行的,比如CMS垃圾收集器的年轻代就是并行的, ... 
- 一篇文章搞懂filebeat(ELK)
		本文使用的filebeat是7.7.0的版本本文从如下几个方面说明: filebeat是什么,可以用来干嘛 filebeat的原理是怎样的,怎么构成的 filebeat应该怎么玩 一.filebeat ... 
- 五分钟学Java:一篇文章搞懂spring和springMVC
		原创声明 本文作者:黄小斜 转载请务必在文章开头注明出处和作者. 本文思维导图 什么是Spring,为什么你要学习spring? 你第一次接触spring框架是在什么时候?相信很多人和我一样,第一次了 ... 
- Java多线程详解——一篇文章搞懂Java多线程
		目录 1. 基本概念 2. 线程的创建和启动 2.1. 多线程实现的原理 2.2.多线程的创建,方式一:继承于Thread类 2.3.多线程的创建,方式一:创建Thread匿名子类(也属于方法一) 2 ... 
- 一篇文章搞懂Android组件化
		网上组件化的文章很多,我本人学习组建化的过程也借鉴了网上先辈们的文章.但大多数文章都从底层的细枝末节开始讲述,由下而上给人一种这门技术“博大精深”望而生畏的感觉.而我写这篇文章的初衷就是由上而下,希望 ... 
随机推荐
- 【Oracle】常见等待事件处理
			1.查看数据库中需要关注的等待事件: select sw.seq#,sw.sid||','||s.serial# sids,s.username,sw.event,sw.P1,sw.p2,sw.p3, ... 
- 【Oracle】增量备份和全库备份怎么恢复数据库
			1差异增量实验示例 1.1差异增量备份 为了演示增量备份的效果,我们在执行一次0级别的备份后,对数据库进行一些改变. 再执行一次1级别的差异增量备份: 执行完1级别的备份后再次对数据库进行更改: 再执 ... 
- 以事实驳斥:改进你的c#代码的5个技巧(四)
			测试使用的环境:vs2019+.net core3.1 原文地址:https://www.cnblogs.com/hhhnicvscs/p/14296715.html 反驳第一条:如何检查代码中的空字 ... 
- 大文件上传FTP
			需求 将本地大文件通过浏览器上传到FTP服务器. 原有方法 将本地文件整个上传到浏览器,然后发送到node服务器,最后由node发送到FTP服务器. 存在问题 浏览器缓存有限且上传速率受网速影响,当文 ... 
- LeetCode349. 两个数组的交集
			题目 给定两个数组,编写一个函数来计算它们的交集. 分析 数组元素值可以很大,所以不适合直接开数组进行哈希,这里要学习另一种哈希方式:集合 集合有三种,区别见下面代码随想录的Carl大佬的表格,总结的 ... 
- SSTI
			最牛bypass:https://blog.csdn.net/solitudi/article/details/107752717 SSTI的奇怪绕过姿势:https://blog.csdn.net/ ... 
- 在Firefox上使用Chrome的crx扩展程序
			假如你喜欢使用Firefox火狐浏览器,可是发现有个很喜欢很想用的扩展只发布了支持Chrome的crx格式--Firefox从57版以后使用了WebExtension API作为新附加组件的开发标准, ... 
- 发票校验BAPI_INCOMINGINVOICE_CREATE
			CALL FUNCTION 'BAPI_INCOMINGINVOICE_CREATE' EXPORTING headerdata = headerdata ... 
- Python爬虫学习笔记(一)
			概念: 使用代码模拟用户,批量发送网络请求,批量获取数据. 分类: 通用爬虫: 通用爬虫是搜索引擎(Baidu.Google.Yahoo等)"抓取系统"的重要组成部分. 主要目的是 ... 
- day131:2RenMJ:2RenMJ游戏简介&部署MJ项目到本地
			目录 1.游戏简介 1.如何做出一款麻将游戏? 2.麻将运行界面 3.麻将项目所用技术快速概览 4.web开发 / 游戏开发 / APP开发 比较 5.firefly游戏框架介绍 2.部署麻将项目到本 ... 
