垃圾收集算法和CMS详解

news2024/11/17 22:23:49

一、垃圾收集算法

1、分带收集理论

基于新生代和老年代选择不同垃圾回收算法,比如新生代,都是一些暂存对象,而且内存分区域的,可以采用标记复制算法。而老年代只有一块内存区域,使用复制算法比较占用内存空间,不适合复制算法,可以使用标记清除或者标记整理,标记复制比标记清除和标记整理要快(可以这么想,复制算法只需要把标记存活对象,然后转移;标记清除和标记整理都需要先标记然后找到垃圾对象进行清除,标记整理最后还要整理)

2、标记-复制算法

把一块内存区域一分为二,一块是已使用内存区域,另一块是未使用内存区域,然后在已使用区域,根据根可达算法,gc标记存活的对象,把存活的对象复制到未使用的内存区域,原来的已使用内存区域直接清空(需要一半内存空间,对内存空间有要求)

3、标记-清除算法

在一块内存区域上先标记,分为两种:一种是标记存活的对象,把未标记的进行回收(一般选这种);另外一种是专门找那些垃圾对象,然后进行清理

要根据实际业务情况进行选择,垃圾对象多那就选择标记存活的对象,不过一般选择标记存活的对象如果比较多的话会比较耗费实际,另外一个就是清理之后内存不连续

4、标记-整理算法

标记过程和标记清除算法一样,但是后面的整理是所有存活的对象往一边移动,有垃圾对象占用直接替换,另外的垃圾对象再清理

二、垃圾收集器

 CMS红线连接到Seria Old表示如果CMS出现“concurrent mode failure”,就会用到Serial Old进行收集,直到收集到有足够可用空间才切换到CMS,黑线相互连接都是表示可以年轻代老年代的垃圾收集器相互配合使用的

1、Serial收集器(-XX:+UseSerialGC -XX:+UseSerialOldGC)

gc过程是暂停所有用户线程,用一个单线程进行垃圾收集,直到垃圾收集结束用户线程才能继续

新生代使用标记复制算法,老年使用标记整理算法

2、Parallel Scavenge收集器(-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代))【jdk1.8默认垃圾收集器】

在serial垃圾收集器版本做的提升(线程版本),可以设置收集线程数(-XX:ParallelGCThreads),一般不推荐修改

新生代使用标记复制算法,老年使用标记整理算法

3、ParNew收集器(-XX:+UseParNewGC)

ParNew和Parallel收集器类似,唯一区别是ParNew可以配合CMS收集器使用

新生代使用标记复制算法,老年使用标记整理算法

4、CMS收集器(-XX:+UseConcMarkSweepGC(old))

CMS(Concurrent Mark Sweep),垃圾收集时间延长,但是STW时间缩短,用户线程和gc线程可以同步进行,中间涉及的问题下面会做解释,整体的过程如下:

  1. 初始标记:暂停所有用户线程,根据可达性分析找到gc root直接引用对象(这一步就是为了下一步,如果直接从gc root就开始gc线程和用户线程同时进行,程序会一直创建对象如果都放到老年代,那样永远都标记不完,初始标记就是把源头gc root确定了,即使产生了放到了老年代,等待下一次gc做初始标记就行了),需要STW
  2. 并发标记:从gc直接引用的对象开始往下找其引用的对象,这一步是用户线程和gc线程是同时进行的,可能会导致已经标记过的对象引用发生了变化,不需要STW
  3. 重新标记:修正并发标记中引用被修改的问题,多标没关系,下次gc自然不会再标记了,主要是漏标(三色标记的增量更新算法),会STW(因为记录了新增的引用只能STW来修正,不允许再有引用发生变化)
  4. 并发清理:用户线程和gc线程同时进行,gc开始把未标记的对象做清理操作,这个阶段如果有新对象产生或者垃圾对象又重新被引用都会被标记成黑色不做处理,不需要STW
  5. 并发重置:重置本次gc的标记对象,下次gc好再次标记

它是一款用户体验好,低停顿,但人无完人,但会完蛋,总有不足的地方,有以下几个缺点:

  1. 用户线程会和gc线程抢占资源
  2. 存在浮动垃圾(在并发标记和并发清理阶段产生的垃圾对象)
  3. 使用标记清除会产生大量的内存碎片(设置参数-XX:+UseCMSCompactAtFullCollection可以清除完后做整理)
  4. 在并发标记和并发清理阶段正在做标记和清除,内存还没空出来这个时候从年轻代来了一批对象放到老年代,这个时候放不下,此时full gc正在进行中,不可能再用CMS了,因为CMS是用户线程和gc线程并行收集对象,这个时候出现"concurrent mode failure",会STW,用serial old垃圾收集器来回收

CMS核心参数:

  1. -XX:+UseConcMarkSweepGC:启用cms 
  2. -XX:ConcGCThreads:并发的GC线程数
  3. -XX:+UseCMSCompactAtFullCollection:FullGC之后做压缩整理(减少碎片)
  4. -XX:CMSFullGCsBeforeCompaction:多少次FullGC之后压缩一次,默认是0,代表每次FullGC后都会压缩一次
  5. -XX:CMSInitiatingOccupancyFraction: 当老年代使用达到该比例时会触发FullGC(默认是92,这是百分比)
  6. -XX:+UseCMSInitiatingOccupancyOnly:只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设定的值),如果不指定,JVM仅在第一次使用设定值,后续则会自动调整
  7. -XX:+CMSScavengeBeforeRemark:在CMS GC前启动一次minor gc,降低CMS GC标记阶段(也会对年轻代一起做标记,如果在minor gc就干掉了很多对垃圾对象,标记阶段就会减少一些标记时间)时的开销,一般CMS的GC耗时 80%都在标记阶段
  8. -XX:+CMSParallellnitialMarkEnabled:表示在初始标记的时候多线程执行,缩短STW
  9. -XX:+CMSParallelRemarkEnabled:在重新标记的时候多线程执行,缩短STW

后面的G1和ZGC后面博客再做解释

三、垃圾收集底层算法

1、三色标记

主要用来解决并发标记中漏标的问题

  • 黑色:表示对象已经被垃圾收集器访问过,且这个对象的所有引用都被扫描过
  • 灰色:表示对象已经被垃圾收集器访问过,但这个对象上至少存在一个引用
  • 白色:表示对象尚未被垃圾收集器访问过,开始阶段所有对象都是白色,结束时若对象还为白色,表示对象不可达

2、多标-浮动垃圾

在并发标记的过程中,用户线程和gc线程同时进行,方法结束gc root被回收,gc线程基于gc root往下标记的引用对象就称为浮动垃圾,等到下一轮gc自然就会被标记回收

并发整理阶段有新的对象产生或者原本是垃圾对象被引用了成为非垃圾对象、非垃圾对象引用突然断了成为垃圾对象,通常做法是直接标记成黑色,这阶段也会产生浮动垃圾

3、漏标-读写屏障

因为漏标产生的误删除有两种解决方案:增量更新(Incremental Update)和原始快照(Snapshot At The Beginning, SATB)

  • 增量更新:当黑色对象插入新的指向白色对象的引用关系时,就将这个新插入的引用记录下来,等并发扫描结束后,再将这些记录过的引用关系中的黑色对象为根,重新扫描一次。这可以简化理解为,黑色对象一旦插入了指向白色对象的引用之后,它就变回灰色对象了
  • 原始快照:当灰色对象要删除指向白色对象的引用关系时,就将这个要删除的引用记录下来,在并发扫描结束之后,再将这些记录过的引用关系中的灰色对象为根,重新扫描一次,这样就能扫描到白色的对象,将白色对象直接标记为黑色(有记录是删除的,不知道有没有其它对象指向这个已删除记录,现在记录下来就是为了让这种对象在本轮gc清理中能够存活下来,也可能是浮动,如果是的话下轮gc被回收)

以上无论是对引用关系记录的插入还是删除,虚拟机的记录操作都是通过写屏障实现的

4、写屏障

参考AOP的概念,就是在写操作前后加入一些屏障操作(屏障操作异步进行,不能影响正常程序写操作),CMS处理漏标问题是采用三色标记的增量更新,G1处理漏标问题是采用三色标记的原始快照

5、读屏障

读屏障是在读操作之前用一个屏障操作记录读取到值

  • CMS:写屏障 + 增量更新
  • G1,Shenandoah:写屏障 + SATB
  • ZGC:读屏障

6、为什么G1用SATB?CMS用增量更新?

增量更新是记录新增的引用,当然可以找到引用白色对象的黑色对象来进行扫描;而原始快照记录的是删除的值,不确定有没有对象会引用这个删除引用的对象;

另一种解释是G1和CMS的内存结构分布不同,如果G1也用增量更新,G1中很多对象位于不同的region区域,而CMS只有一块老年代内存区域,重新扫描的话G1扫描会更慢,所以G1只先记录一下,等待下一次GC回收

四、记忆集和卡表、安全点和安全区域

1、记忆集和卡表

在gc过程中如果存在跨代引用的对象,如果再跨代去扫描那样效率太低了

记忆集(Remember Set):记录非收集区到收集区引用指针的集合

卡表是对记忆集的实现,卡表中的元素对应着其标识的内存区域是一块512M内存的卡页,一个卡页中包含多个对象,只要有一个对象存在跨代引用,其对应的卡表的元素标识就变成1,表示该元素变脏,否则为0

GC时,只要筛选本收集区的卡表中变脏的元素加入GCRoots里

卡表在收集区,卡页在非收集区,Hotspot使用写屏障维护卡表状态

2、安全点

垃圾回收时都需要进行stw,不可能线程执行程序马上中断,有些原子性操作中断可能会出问题,这样就需要设立标识,当达到这个安全点的时候,就可以进行gc操作;这里不是去中断线程,而是设立一个标识让线程挂起;安全点:方法返回前、调用某个方法之后、抛出异常位置、循环的末尾

3、安全区域

是指在一段代码执行过程中引用关系不会发生变化

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

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

相关文章

DEVICENET转ETHERCAT网关连接ethercat通讯协议详细解析

你有没有遇到过生产管理系统中,设备之间的通讯问题?两个不同协议的设备进行通讯,是不是很麻烦?今天,我们为大家介绍一款神奇的产品,能够将不同协议的设备进行连接,让现场的数据交换不再困扰&…

MySQL数据库 - 库的操作

目录​​​​​​​ 一、创建数据库 二、创建数据库案例 三、字符集和校验规则 四、校验规则对数据库的影响 五、操纵数据库 1、查看数据库 2、显示创建语句 3、修改数据库 4、删除数据库 六、数据库的备份与恢复 1、数据库的备份 2、数据库的恢复 3、表的备份 4…

【网络系统集成】Pfsense防火墙实验

1.实验名称 Pfsense防火墙实验 2.实验目的 通过动手实践配置pfsense对加深对防火墙的原理与应用的理解。 3.实验内容 (1)安装并完成pfsense防火墙软件的基本配置(WAN, LAN,局域网

刘积仁:东软不太喜欢风口,更看重长期主义

作为数字和软件服务产业一年一度的行业盛宴,2003年,中国国际软件和信息服务交易会(简称“软交会”)正式诞生。2019年,大会更名为中国国际数字和软件服务交易会(简称“数交会”),至今…

【C++修炼之路】string 概述

👑作者主页:安 度 因 🏠学习社区:StackFrame 📖专栏链接:C修炼之路 文章目录 一、string 为何使用模板二、string 类认识1、构造/析构/赋值运算符重载2、容量操作3、增删查改4、遍历5、迭代器6、非成员函数…

[NSSRound#13 Basic]flask?jwt?解题思路过程

过程 打开题目链接,是一个登录框,不加验证码,且在注册用户名admin时提示该用户名已被注册,因此爆破也是一种思路。不过根据题目名字中的提示,jwt,且拥有注册入口,注册一个用户先。 注册完用户…

Flink DataStream之使用filter实现分流

新建类 package test01;import org.apache.flink.api.common.JobExecutionResult; import org.apache.flink.configuration.Configuration; import org.apache.flink.streaming.api.datastream.DataStreamSource; import org.apache.flink.streaming.api.datastream.SingleOut…

Pygame Zero(pgzrun)游戏库介绍

Pygame Zero(pgzrun)游戏库介绍 pgzero是python的一个第三方库。pgzrun 是 python game zero run 的缩写, 它对 Pygame 进行了封装, 屏蔽了繁琐枯燥的框架代码, 让学习者可以更专注于游戏的实现逻辑, 并且更快看到成果。 官网https://pygame-zero.read…

单样本微调给ChatGLM2注入知识~

前方干货预警:这可能也是一篇会改变你对LLM微调范式,以及对LLM原理理解的文章。 同时这也是一篇非常有趣好玩,具有强大实操性的ChatGLM2微调喂饭级教程。 我们演示了使用AdaLoRA算法,使用1条样本对ChatGLM2-6b实施微调。几分钟就成…

【Redis】五大数据类型(操作命令)

🎯Redis 命令 🚩Redis 键(key) 这些是 Redis 数据库中的命令,用于对数据类型进行操作和管理。以下是每个命令的含义和用法: DEL:删除一个或多个键。DUMP:将一个键的值转储到一个字符串中。EXPIRE&#x…

【数据结构二叉树OJ系列】4、翻转二叉树(又称求二叉树的镜像)

目录 法一、 法二、 题述: 翻转一颗二叉树。 输入: 输出: 题中已给: struct TreeNode {int val;struct TreeNode* left;struct TreeNode* right; }; TreeNode* invertTree(struct TreeNode* root) 法一、 思路:…

操作指南 | 如何使用Foundry在Moonbeam上进行部署

Foundry是一种以太坊开发环境,可帮助构建者管理依赖项、编译项目、测试或部署合约以及通过指令与区块链进行交互。Foundry已成为流行的开发智能合约开发环境,仅需要使用Solidity即可进行操作。Moonbeam在官方文档网站提供了有关将Foundry与Moonbeam网络结…

vector [] 赋值出现的报错问题

下面这段代码的作用是创建了一个整数类型的vector&#xff08;std::vector<int>&#xff09;并对其进行操作。以下是代码的详细说明&#xff1a; 使用reserve(10)方法为向量分配至少10个元素的存储空间。reserve() 预留了额外的存储空间&#xff0c;以避免后续添加元素时…

C++之typeof和typeid用法(一百五十三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

动态规划--Fibonacci数列 III

描述 众所周知&#xff0c;Fibonacci数列是一个著名数列。它的定义是&#xff1a; 本题要求采用第三种方法&#xff1a;简单的动态规划。 用数组把求出来的 Fibonacci 数列保存下来&#xff0c;以免后面要的时候再算一次。 输入描述 每行一个整数 i &#xff0c;表示 Fibona…

【C++修炼之路】string 模拟实现

&#x1f451;作者主页&#xff1a;安 度 因 &#x1f3e0;学习社区&#xff1a;StackFrame &#x1f4d6;专栏链接&#xff1a;C修炼之路 文章目录 一、默认成员函数1、全缺省构造2、析构3、拷贝构造&#xff08;深拷贝&#xff09;4、赋值重载&#xff08;深拷贝&#xff09;…

Langchain 新手完全指南

Langchain 可能是目前在 AI 领域中最热门的事物之一&#xff0c;仅次于向量数据库。 它是一个框架&#xff0c;用于在大型语言模型上开发应用程序&#xff0c;例如 GPT、LLama、Hugging Face 模型等。 它最初是一个 Python 包&#xff0c;但现在也有一个 TypeScript 版本&…

Git gui教程---第五篇 Git gui的使用 查看提交历史

查看提交历史 1.点击菜单栏的“版本库”&#xff0c;选择“图示master分支的历史” 2.出现的界面就是显示当前分支的提交历史了

Java基础---Java中创建对象方式

目录 使用new关键字 使用反射机制 使用clone方法 使用反序列化 使用方法句柄 使用Unsafe分配内存 使用new关键字 这是最常见的也是最简单的创建对象的方式通过这种方式还可以调用任意的构造函数&#xff08;无参的和有参的&#xff09; 使用反射机制 运用反射手段&#…

单个电源模块带电感的直流压降仿真(一)

单个电源模块带电感的直流压降仿真(一) 下面实例分析单个电源模块带电感的直流压降仿真分析,以下图为例 具体操作如下 创建新的workspaceLoad a New/Different layout(把PCB文件加载进来)