JVM系列 | 对象的消亡3——垃圾收集器的对比与实现细节

news2024/11/15 18:18:54

垃圾收集器

文章目录

  • 各收集器简单对比
  • 收集器启动参数
  • 各收集器详细说明
    • JDK 1.3 之前
    • JDK 1.3 | Serial
    • JDK 1.4 | ParNew
    • JDK 1.4 | Parallel Scavenge
    • JDK 5 | CMS 收集器
    • JDK 7 | G1

各收集器简单对比

收集器名称出现时间淘汰时间目标采用技术线程数STW分代备注
无名JDK 1.3之前JDK 1.3标记清除1不分代
SerialJDK 1.3至今仍作为客户端默认的收集器标记复制1新生代/老年代由于简单而高效,仍作为客户端系统的默认垃圾收集器。
ParNewJDK 1.4自JDK9开始只能与CMS搭配只用高效的回收垃圾标记复制默认与核心线程数一样多新生代可以看做是Serial的多线程版本
Parallel ScavengeJDK 1.4至今仍活跃,尤其是需要吞吐量的场景,如大量计算等最大吞吐量标记复制默认与核心线程数一样多新生代吞吐量优先收集器(吞吐量需满足下方公式)
CMSJDK 5JDK9标记为过时/JDK14被正式移除最短回收停顿时间标记清除(处理器核心数量+3)/4老年代第一个可以与用户线程同时工作的垃圾收集器,工作时长更长了,但是用户线程停顿更短饿了
Serial Old不明确/1.2之前JDK之前与Parallel Scavenge,现在一般作为CMS失败的备用方案标记整理1老年代Serial的老年版
Parallel OldJDK 6至今最大吞吐量标记整理N老年代Parallel Scavenge的老年斑
G1/Garbage FirstJDK 7至今时间可控/全功能标记复制NRegion分区G1淘汰了CMS
ShenandoahOpenJDK 12至今10毫秒内垃圾收集标记整理NRegion分区RedHat开发;JDK中没有,只有OpenJDK中才有
ZGCJDK 11至今10毫秒内垃圾收集标记整理NRegion分区使用了读屏障、染色指针和内存多重映射等技术

名词解释:STW(Stop The World)指停止用户线程,该值为否则表示可以与用户线程同时进行。(也许不是整个清理阶段都与用户线程并行,可能只是其中某些阶段)

吞吐量 = 运行用户代码时间 运行用户代码时间 + 运行垃圾收集时间 吞吐量={运行用户代码时间\over{运行用户代码时间+运行垃圾收集时间}} 吞吐量=运行用户代码时间+运行垃圾收集时间运行用户代码时间




收集器启动参数

在本部分中有一些启动项目的命令,这里采用Github开源项目·Zfile进行示例。

收集器名称启动该收集器
Serial-XX:+UseSerialGC
ParNew-XX:+UseParNewGC
Parallel Scavenge-XX:+UseParallelGC
CMS-XX:+UseConcMarkSweepGC
Serial Old使用Serial收集器时,会自动启动Serial Old收集器
Parallel Old-XX:+UseParallelOldGC
G1/Garbage First-XX:+UseG1GC
Shenandoah-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC

# Serial
java -XX:+UseSerialGC -jar zfile.jar

# ParNew
java -XX:+UseParNewGC -jar zfile.jar 

# ParNew + CMS
java -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -jar zfile.jar

# Parallel Scavenge
java -XX:+UseParallelGC -jar zfile.jar
java -XX:+UseParallelGC -XX:GCTimeRatio=1 -jar zfile.jar # 垃圾收集器占比不超过1%
java -XX:+UseParallelGC -XX:+UseAdaptiveSizePolicy -jar zfile.jar # 虚拟机优化内存

# CMS
java -XX:+UseConcMarkSweepGC -jar zfile.jar

# Parallel Old收集器
java -XX:+UseParallelOldGC -jar zfile.jar

# G1
java -XX:+UseG1GC -jar zfile.jar

# Shenandoah | 由于现在还在测试阶段,需要使用 -XX:+UnlockExperimentalVMOptions
java -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -jar zfile.jar




各收集器详细说明


JDK 1.3 之前

JDK 1.3 之前并没有一个真正像样的垃圾收集器(据说Serial Old是1.2之前出现的,其余不详),那个时代的垃圾收集器甚至没有一个名字,资料只能搜索到“标记-清除垃圾收集器”,很明显是基于标记清除算法实现的,特点是单线程,需要停止用户线程,没有分代,效率低下等…


JDK 1.3 | Serial

Serial收集器采用标记-清除算法,单线程处理,在进行垃圾收集的时候必须停止用户线程(STW/Stop The World),Serial收集的比较“慢”,这也是一开始都诟病Java慢的主要原因。

但是,Serial的优点是简单,它不需要太多的内存空间,也没有线程切换的资源消耗,就像是一个扫地僧一样默默地进行着自己的工作。而且虽然“慢”,但是一般来说也就只有几十~上百毫秒,虽然这个时间对于服务端程序来说可能十分宝贵,但是对于客户端程序来说无伤大雅,可能你使用的Java App一直每隔一段时间暂停几十毫秒,而你根本察觉不到。正是由于它简单(不消耗太多内存等)高效(相对于其它多线程版本的一条线程来说),所以Serial被广泛使用在客户端程序中。

Serial收集器的工作过程如下(图片来源于《深入理解Java虚拟机》):

在这里插入图片描述

可以看出,每次需要进行垃圾收集的时候,都需要停顿用户线程,且每次只有一个线程在进行垃圾收集


JDK 1.4 | ParNew

ParNew收集器是Serial收集器的多线程版本,在操作上除了是采用多线程的方式来进行的以外,其余与Serial区别不大。需要注意的是:由于ParNew还需要有现成切换的成本,所以其单线程的效率是比Serial低的,也就是说核心数为1时,其工作效率不如Serial收集器。

所以ParNew收集器更适合于核心线程数尚且客观(4~6个)的一些老项目中。


JDK 1.4 | Parallel Scavenge

吞吐量公式说明

Parallel Scavenge是一款吞吐量收集器,主要关注程序的吞吐量,首先了解下吞吐量计算公式:

吞吐量 = 运行用户代码时间 运行用户代码时间 + 运行垃圾收集时间 吞吐量={运行用户代码时间\over{运行用户代码时间+运行垃圾收集时间}} 吞吐量=运行用户代码时间+运行垃圾收集时间运行用户代码时间

如果程序运行需要99毫秒,垃圾回收需要1毫秒,那么吞吐量就是 99/(1 + 99) = 99%。

相关参数

我们可以通过下面一些指令来控制吞吐量,也可以直接控制最大垃圾收集停顿时间等,或者直接将这些参数交给虚拟机来进行控制。

指令接收参数说明
-XX:MaxGCPauseMillis大于0的毫秒数控制最大垃圾收集停顿时间
-XX:GCTimeRatio大于0小于100的整数,也就是垃圾收集时间占总时间的比率直接设置吞吐量大小
-XX:+UseAdaptiveSizePolicy允许虚拟机自动对内存进行调优,从而控制吞吐量

JDK 5 | CMS 收集器

CMS简介

CMS收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用集中在互联网网站或者基于浏览器的B/S系统的服务端上,这类应用通常都会较为关注服务的响应速度,希望系统停顿时间尽可能短,以给用户带来良好的交互体验。CMS收集器就非常符合这类应用的需求。

CMS收集器采用标记清除算法,其工作分为四个步骤:

  1. 初始标记:初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快

  2. 并发标记(CMS concurrent mark) 与用户线程同时:并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行

  3. 重新标记(CMS remark):重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短

  4. 并发清除(CMS concurrent sweep) 与用户线程同时:最并发清除阶段,清理删除掉标记阶段判断的已经死亡的对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的

并发清除一定会面临两个问题:浮动垃圾与消失对象问题,CMS采用的是增量更新的解决方案,在之前的博客中已经介绍过了,请点此查看。


CMS的缺点

  1. **处理器资源敏感:**CMS默认启动的回收线程数是(处理器核心数量+3)/4,也就是说,如果处理器核心数在四个或以上,并发回收时垃圾收集线程只占用不超过25%的处理器运算资源,并且会随着处理器核心数量的增加而下降。但是当处理器核心数量不足四个时,CMS对用户程序的影响就可能变得很大。如果应用本来的处理器负载就很高,还要分出一半的运算能力去执行收集器线程,就可能导致用户程序的执行速度忽然大幅降低

  2. Full GC问题:由于CMS收集器无法处理“浮动垃圾”(Floating Garbage),有可能出现“Concurrent Mode Failure”失败进而导致另一次完全“Stop The World”的Full GC的产生(我的理解是由于浮动垃圾的存在导致浪费了一定的空间,并且垃圾收集过程中用户线程还在继续,还在产生新的垃圾,最终内存空间不足导致直接触发FullGC)。因此CMS一般会在内存空间达到一定阈值后直接进行垃圾回收,JDK5默认是68%,JDK6默认为92%,可以通过-XX:CMSInitiatingOccupancyFraction调整阈值大小。

  3. 空间碎片问题:由于CMS是基于标记清除算法进行工作的,这意味着收集结束时会有大量空间碎片产生。空间碎片过多时,将会给大对象分配带来很大麻烦,往往会出现老年代还有很多剩余空间,但就是无法找到足够大的连续空间来分配当前对象,而不得不提前触发一次Full GC的情况。


CMS运行示意图

在这里插入图片描述



JDK 7 | G1

G1收集器具有里程碑式意义,它开创了收集器面向局部收集的设计思路和基于Region的内存布局形式。简单来说,传统的垃圾收集器都是一个老年代,一个新生代,老年代用来存放存活时间较长的对象(或一些大对象),新生代用来存放新对象。而G1收集器采用的是

在G1收集器出现之前的所有其他收集器,包括CMS在内,垃圾收集的目标范围要么是整个新生代(Minor GC),要么就是整个老年代(Major GC),再要么就是整个Java堆(Full GC)。而G1跳出了这个樊笼,它可以面向堆内存任何部分来组成回收集(Collection Set,一般简称CSet)进行回收,衡量标准不再是它属于哪个分代,而是哪块内存中存放的垃圾数量最多,回收收益最大,这就是G1收集器的Mixed GC模式。

简单来说,G1收集器不再是传统的老年代与新生代,而是将连续的Java堆划分为多个大小相等的独立区域(Region),每一个Region都可以根据需要扮演老年代、新生代的角色。G1收集器会根据

Region是最小的的单位,每次垃圾收集的内存空间一定是Region的整数倍。G1会根据回收收益情况,判断回收哪些Region空间。

Region中还有一类特殊的Humongous区域,**专门用来存储大对象。**G1认为只要大小超过了一个Region容量一半的对象即可判定为大对象。每个Region的大小可以通过参数-XX:G1HeapRegionSize设定,取值范围为1MB~32MB,且应为2的N次幂。而对于那些超过了整个Region容量的超级大对象,将会被存放在N个连续的Humongous Region之中,G1的大多数行为都把Humongous Region作为老年代的一部分来进行看待。

在这里插入图片描述


产生的问题与解决方案

**跨区引用问题:**每个Region都维护有自己的记忆集,这些记忆集会记录下别的Region指向自己的指针,并标记这些指针分别在哪些卡页的范围之内。G1的记忆集在存储结构的本质上是一种哈希表,Key是别的Region的起始地址,Value是一个集合,里面存储的元素是卡表的索引号。这种“双向”的卡表结构(卡表是“我指向谁”,这种结构还记录了“谁指向我”)比原来的卡表实现起来更复杂,同时由于Region数量比传统收集器的分代数量明显要多得多,因此G1收集器要比其他的传统垃圾收集器有着更高的内存占用负担。

**收集线程与用户线程互不干扰的运行:**首先要解决的是用户线程改变对象引用关系时,必须保证其不能打破原本的对象图结构,导致标记结果出现错误(见《HotSpot实现细节-原是快照》);此外,垃圾收集对用户线程的影响还体现在回收过程中新创建对象的内存分配上,程序要继续运行就肯定会持续有新对象被创建,G1为每一个Region设计了两个名为TAMS(Top at Mark Start)的指针,把Region中的一部分空间划分出来用于并发回收过程中的新对象分配,并发回收时新分配的对象地址都必须要在这两个指针位置以上。G1收集器默认在这个地址以上的对象是被隐式标记过的,即默认它们是存活的,不纳入回收范围。与CMS中的“Concurrent Mode Failure”失败会导致Full GC类似,如果内存回收的速度赶不上内存分配的速度,G1收集器也要被迫冻结用户线程执行,导致Full GC而产生长时间“Stop The World”。

**怎样建立起可靠的停顿预测模型?**用户通过-XX:MaxGCPauseMillis参数指定的停顿时间只意味着垃圾收集发生之前的期望值,但G1收集器要怎么做才能满足用户的期望呢?G1收集器的停顿预测模型是以衰减均值(Decaying Average)为理论基础来实现的,在垃圾收集过程中,G1收集器会记录每个Region的回收耗时、每个Region记忆集里的脏卡数量等各个可测量的步骤花费的成本,并分析得出平均值、标准偏差、置信度等统计信息。这里强调的“衰减平均值”是指它会比普通的平均值更容易受到新数据的影响,平均值代表整体平均状态,但衰减平均值更准确地代表“最近的”平均状态。换句话说,Region的统计状态越新越能决定其回收的价值。然后通过这些信息预测现在开始回收的话,由哪些Region组成回收集才可以在不超过期望停顿时间的约束下获得最高的收益。


回收过程

  1. **初始标记(Initial Marking):**仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region中分配新对象。这个阶段需要停顿线程,但耗时很短,而且是借用进行Minor GC的时候同步完成的,所以G1收集器在这个阶段实际并没有额外的停顿。

  2. **并发标记(Concurrent Marking):**从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。当对象图扫描完成以后,还要重新处理SATB记录下的在并发时有引用变动的对象。

  3. **最终标记(Final Marking):**对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的最后那少量的SATB记录。

  4. **筛选回收(Live Data Counting and Evacuation):**负责更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧Region的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行完成的。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1982199.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android 自定义View(一):View是什么?如何创建自定义view,自定义属性等

目录 1)View是什么? 2)View分类 3)View的知识点 4)View的工作流程是怎么样的? 5)案例:如何自定义View?比如我们要实现一个输入框带有清除按钮的view 6)疑问&…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 免单统计(100分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM金牌🏅️团队| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题…

0805作业+梳理

一、作业&#xff1a; 代码&#xff1a; create.c #include<myhead.h> int main(int argc, const char *argv[]) {//创建一个有名管道文件if(mkfifo("./linux",0664)-1){perror("mkfifo linux error");return -1;}getchar();system("rm linux…

8.15 C++作业

输入一组字符&#xff0c;实现各字符的归类统计 #include <iostream> #include <string.h>using namespace std;namespace xiaoli {string str;int len; } using namespace xiaoli;int main() {getline(cin,str);//识别空格len str.size();int a0,b0,c0,d0,e0;fo…

x-cmd mod | x yq - 轻量级的 YAML、JSON、XML 处理器

目录 简介使用语法参数子命令x yq repl 简介 yq (YAML Query) 是一个轻量级的 YAML、JSON、XML 处理器&#xff0c;主要用于查询和提取 YAML 数据。 x yq 是基于 yq 命令、为提升 yq 使用体验而设计的增强模块&#xff0c;具体的增强性改动如下&#xff1a; 简化 yq 命令的安…

【GoodERP更新日志】增加模块 质检管理 处理来料检、发货检功能

开源项目GoodERP更新-2024年8月5日 本次提交合并增加的功能或解决的问题&#xff1a;增加 质量管理模块->增加来料质检、发货质检功能 提交前: 无 提交后: 1、按供应商来料&#xff0c;安排来料质检工作&#xff1b; 2、按销售订单&#xff0c;安排对计划发货商品进行…

比较推荐哪种可视耳勺?5大热门产品专业测评分享!

由于现在人们对健康生活越来越重视&#xff0c;可视耳勺成为了个护健康产品&#xff0c;受到了越来越多消费者的关注和喜爱。这种挖耳勺采用了先进的无线技术和高清摄像头&#xff0c;能够让人们更加清晰地观察自己耳内的状况&#xff0c;从而更加安全、有效地清洁耳朵。但同时…

封装组件之使用vue3封装简易Button按钮

1.新建Button.vue <template><button :class"buttonClass"><!-- 用于输入内容 --><slot></slot></button> </template> <script lang"ts" setup> import { computed } from vue; //定义类型 const props…

OpenGL投影矩阵

OpenGL Projection Matrix OpenGL投影矩阵

大模型检索增强生成RAG

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhlRAG简介 大模型检索增强生成(Retrieval-Augmented Generation,简称RAG)是一种结合了信息检索技术和语言生成模型的人工智能技术,主要用于增强大型语言模型(Large Language Models, LLMs)处理…

能见度监测站在机场中的应用

在繁忙的机场&#xff0c;每一个细节都关乎着飞行安全。其中&#xff0c;能见度作为关键的气象因素&#xff0c;直接影响着飞机的起降安全。为此&#xff0c;能见度监测站在机场中扮演着重要的角色。 能见度监测站利用先进的传感器技术&#xff0c;实时监测机场周边的能见度情况…

【医学图像】医学图像基础

目录 引言 医学成像常识 1. 正交投影面 2 医学图像种类 3 医学图像质量评价标准 3.1 图像对比度成因 3.2 对比度 3.3 空间分辨率 3.4 时间分辨率 3.5 信噪比 signal-to-noise ratio SNR 3.6 数字和模拟图像 4 医疗临床的性能 4.1 图像获取和感知 4.2 灵敏度和特异…

WEB服务器安全加固与检查

01.安全加固定义 什么是安全加固? 安全加固是实现信息系统安全的关键环节。通过安全加固&#xff0c;将在信息系统的网络层、主机层、软件层、应用层等层次建立符合安全需求的安全状态&#xff0c;并以此作为保证网络信息系统安全的起点。 安全加固是配置软件…

使用gitea私有仓库作为依赖

实际问题 由于公司团队使用gitea搭建了git私有仓库&#xff0c;在开发Go程序的时候会有一些公共代码&#xff0c;比如插件和主程序之间要共享接口和数据结构&#xff0c;所以就需要在gitea私有仓库中创建依赖仓库&#xff0c;然后其他仓库引用这个私有仓库作为依赖。 解决方案…

记录|LabVIEW从0开始【09~10】

目录 前言一、属性节点案例&#xff1a;Step1. 表格控件设置Step2. 下拉列表控制Step1~Step2 效果展示Step3. 编写事件分支分支1&#xff1a;前面板关闭&#xff1f;分支2&#xff1a;表格&#xff0c;鼠标按下创建分支创建表格引用获得单元格的2种位置。设置下拉框的位置下拉框…

【秋招笔试题】矿脉开采(树形DP)

此题直接按树形dp做即可&#xff0c;每次从0枚举到k转移状态 #include <iostream> #include <cstring> #include <algorithm>using namespace std; #define endl \n #define lson node << 1 #define rson node << 1 | 1 const int maxn 1e5 5…

未授权访问漏洞系列详解⑥!

JBoss未授权访问漏洞 JBoss是一个基于J2EE的开放源代码应用服务器&#xff0c;代码遵循LGPL许可&#xff0c;可以在任何商业应用中免费使用;JBoss也是一个管理EJB的容器和服务器&#xff0c;支持EJB1.1、EJB 2.0和EJB3规范。,默认情况下访问 http://ip:8080/jmx-console 就可以…

宠物空气净化器哪个牌子好?希喂、霍尼韦尔宠物空气净化器对比推荐

随着养宠物人群变多&#xff0c;现在打着宠物专用旗号的空气净化器也越来越多&#xff0c;但是很多空气净化器对宠物的效果&#xff0c;真的是只有宣传上和宠物有关&#xff0c;实际设计和效果上和宠物毫无关系。需要大家擦亮眼睛&#xff0c;多做功课&#xff0c;才能不被那些…

PXE+kickstart实现无人值守自动安装操作系统

PXEkickstart实现无人值守自动安装操作系统 让待安装系统的主机自动安装系统&#xff0c;并且自动的安装kickstart文件安装系统&#xff0c;不需要人工干预&#xff0c;直接自动化批量安装操作系统 文章目录 PXEkickstart实现无人值守自动安装操作系统在VMware虚拟机中进行操作…

Day19 标准IO的学习

标准IO 1.I input 输入 键盘 O output 输出 显示器 2.一般我们调用相关IO操作时必须调用stdio头文件库&#xff0c;其位置在/usr/include/stdio.h&#xff0c;linux系统中最高管理者是root。 stdio.h ~ stdio.c ~ libc.so ~ /usr/lib so动态库 3.…