JVM系列(十一) 垃圾收集器之 Concurrent Mark Sweep 并发标记清除

news2024/12/26 11:33:27

垃圾收集器之 Concurrent Mark Sweep 并发标记清除

上几篇文章我们讲解了单线程垃圾收集器 Serial/SerialOld ,多线程垃圾收集器 Parallel Scavenge/Old, 本文我们讲解下 Concurrent Mark Sweep 简称CMS垃圾收集器

垃圾收集器

  • 新生代收集器: Serial、ParNew、Parallel Scavenge;
  • 老年代收集器: Serial Old、CMS、Parallel Old;
  • 通用收集器: G1;

收集器常用组合:

  1. Serial + Serial Old JVM设置-XX:+UseSerialGC
  2. Parallel Scavenge + Parallel Old JVM设置-XX:+UseParallelGC -XX:-UseParallelOldGC
  3. ParNew + CMS配合 JVM设置-XX:+UseParNewGC -XX:+UseConcMarkSweepGC
  4. G1(不需要组合其他收集器) JVM设置-XX:+UseG1GC

今天我们要讲的就是 Concurrent Mark Sweep 收集器

1.CMS Concurrent Mark Sweep标记清除收集器

  • 它是一种以获取最短回收停顿时间为目标的收集器。
  • 它非常符合在注重用户体验的应用上使用,是一款真正意义上的并发收集器
  • 它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。
1.1 CMS垃圾收集器工作流程

CMS执行垃圾清除算法可以分为以下7个步骤:

  • 初始标记(STW)
  • 并发标记
  • 预处理
  • 可中断的预处理
  • 重新标记(STW)
  • 并发清除
  • 并发重置

image.png

下面我们详细讲解下CMS采用标记清除算法

运行过程分为五步骤:

    1. 初始标记:会暂停其他线程STW,从gc root开始,只标记gc root能直接引用的对象,速度很快
    1. 并发标记:这个阶段会从gc root往下遍历所有关联的对象,耗时很长,但是不需要用户线程停顿,可以和用户线程同时一起执行,用户感知不到,但是可能会导致已经标记过的对象状态发生改变
      • 比如你第一次标记了A,现在用户再次操作A的标记已经变了,所以A对象后面需要重新标记,这就引入了第三阶段
    1. 预处理 参数 CMSPrecleaningEnabled 选择关闭该阶段,默认启用
    • 处理新生代已经发现的引用,比如在并发阶段,在Eden区中分配了一个A对象,A对象引用了一个老年代对象B(这个B之前没有被标记),在这个阶段就会标记对象B为活跃对象。
    • 在并发标记阶段,如果老年代中有对象内部引用发生变化,会重新标记那些在并发标记阶段引用被更新的对象
  • 4.可中断的预清理 CMSScheduleRemarkEdenSizeThreshold控制是否需要该阶段
    • 新生代的对象太少,就没有必要执行该阶段,直接执行重新标记阶段
    1. 重新标记:这个阶段是为了修正之前用户线程运行产生变动的标记记录,主要就是为了处理漏标的问题,这个阶段会暂停用户线程STW,暂时时间比初始标记阶段长,但是比并标记阶段时间短
      • 该阶段采用三色标记里的增量更新算法做重新标记
  • 6.并发清理:开启用户线程,此阶段用户线程和垃圾回收线程同时执行,开始回收垃圾对象,gc开始清理未标记的区域,这个阶段如果有新增的对象,会被标记为黑色且不做任何处理
  • 7.并发重置:重置gc过程的标记数据,为下一次gc做准备
1.2 CMS垃圾收集器的优缺点
  • 优点
    • 并发收集垃圾,停顿时间短,第一次真正意义上的并发
    • 延迟较低,用户体验较好
  • 缺点
    • 竞争服务器资源,因为它收集线程和用户线程同时执行,互相抢占CPU资源,加剧CPU轮转切换
    • 并发标记和并发清理阶段,依旧会有浮动垃圾,无法处理,甚至会导致FullGC
    • 采用标记清除算法,会产生大量内存碎片,导致大对象无法分配不得不提前触发FullGC

2.CMS JVM参数配置

  • -XX:+UseConcMarkSweepGC
    • 配置启用CMS
  • -XX:+UseCMSCompactAtFullCollection
    • FullGC之后做压缩整理(减少碎片),针对标记清除算法的优化
  • -XX:CMSFullGCsBeforeCompaction
    • 多少次FullGC之后压缩一次,默认是0,代表每次FullGC后都会压缩一次
  • -XX:CMSInitiatingOccupancyFraction
    • 设置老年代的阈值,当达到阈值时会触发Full GC,默认时92%
2.1 CMS测试

设置JVM参数,启用CMS垃圾收集器

-verbose:gc -XX:+UseConcMarkSweepGC -Xms10M -Xmx10M  -XX:+PrintGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:SurvivorRatio=8

CMSTest测试类,测试

@Slf4j
public class CMSTest {

    //JVM 参数 -verbose:gc -Xms10M -Xmx10M  -XX:+PrintGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:SurvivorRatio=8
    public static void main(String[] args) throws Exception {
        byte[] b = null;
        for (int i = 1; i <= 10; i++) {
            //设置 1M的对象
            log.info("======== " + i + "次添加1M对象");
            b = new byte[1 * 1024 * 1024];
            Thread.sleep(100);

        }
    }
}

打印GC日志

[GC (Allocation Failure) [ParNew: 2752K->320K(3072K), 0.0016638 secs] 2752K->916K(9920K), 0.0017058 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [ParNew: 3072K->320K(3072K), 0.0008008 secs] 3668K->1465K(9920K), 0.0008248 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
00:10:15.957 [main] INFO com.jzj.jvmtest.jvmready.CMSTest - ======== 1次添加1M对象
00:10:16.074 [main] INFO com.jzj.jvmtest.jvmready.CMSTest - ======== 2次添加1M对象
[GC (Allocation Failure) [ParNew: 2748K->320K(3072K), 0.0011250 secs] 3894K->2792K(9920K), 0.0011456 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
00:10:16.185 [main] INFO com.jzj.jvmtest.jvmready.CMSTest - ======== 3次添加1M对象
00:10:16.297 [main] INFO com.jzj.jvmtest.jvmready.CMSTest - ======== 4次添加1M对象
[GC (Allocation Failure) [ParNew: 2420K->22K(3072K), 0.0012212 secs] 4893K->3736K(9920K), 0.0012419 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (CMS Initial Mark) [1 CMS-initial-mark: 3714K(6848K)] 4760K(9920K), 0.0004679 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[CMS-concurrent-mark-start]
[CMS-concurrent-mark: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[CMS-concurrent-preclean-start]
[CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (CMS Final Remark) [YG occupancy: 1046 K (3072 K)][Rescan (parallel) , 0.0001200 secs][weak refs processing, 0.0000285 secs][class unloading, 0.0003868 secs][scrub symbol table, 0.0004698 secs][scrub string table, 0.0001029 secs][1 CMS-remark: 3714K(6848K)] 4760K(9920K), 0.0011706 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[CMS-concurrent-sweep-start]
[CMS-concurrent-sweep: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[CMS-concurrent-reset-start]
[CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
00:10:16.406 [main] INFO com.jzj.jvmtest.jvmready.CMSTest - ======== 5次添加1M对象
00:10:16.517 [main] INFO com.jzj.jvmtest.jvmready.CMSTest - ======== 6次添加1M对象
[GC (Allocation Failure) [ParNew: 2123K->11K(3072K), 0.0005590 secs] 3491K->2403K(9920K), 0.0006038 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
00:10:16.629 [main] INFO com.jzj.jvmtest.jvmready.CMSTest - ======== 7次添加1M对象
00:10:16.741 [main] INFO com.jzj.jvmtest.jvmready.CMSTest - ======== 8次添加1M对象
[GC (Allocation Failure) [ParNew: 2112K->2K(3072K), 0.0004189 secs] 4504K->3418K(9920K), 0.0004425 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
00:10:16.853 [main] INFO com.jzj.jvmtest.jvmready.CMSTest - ======== 9次添加1M对象
00:10:16.963 [main] INFO com.jzj.jvmtest.jvmready.CMSTest - ======== 10次添加1M对象
[GC (Allocation Failure) [ParNew: 2533K->139K(3072K), 0.0007700 secs] 5949K->4580K(9920K), 0.0007987 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 par new generation   total 3072K, used 1191K [0x00000000ff600000, 0x00000000ff950000, 0x00000000ff950000)
  eden space 2752K,  38% used [0x00000000ff600000, 0x00000000ff706f98, 0x00000000ff8b0000)
  from space 320K,  43% used [0x00000000ff900000, 0x00000000ff922f78, 0x00000000ff950000)
  to   space 320K,   0% used [0x00000000ff8b0000, 0x00000000ff8b0000, 0x00000000ff900000)
 concurrent mark-sweep generation total 6848K, used 4440K [0x00000000ff950000, 0x0000000100000000, 0x0000000100000000)
 Metaspace       used 5178K, capacity 5308K, committed 5504K, reserved 1056768K
  class space    used 574K, capacity 596K, committed 640K, reserved 1048576K

3.GC日志分析

  • par new generation total 3072K 年轻代大小
  • concurrent mark-sweep generation total 6848K 老年代大小
  • Eden space 2752K eden区
  • from space from区 320K
  • to space to区 320K
  • Metaspace 元空间 5308K

CMS GC日志分析 gc执行步骤

  • GC (Allocation Failure) ParNew 新生代采用的是ParNew 收集器来收集新生代垃圾CMS-concurrent-sweep-start
  • GC (CMS Initial Mark) 第一步 初始标记 会发生STW
  • CMS-concurrent-mark-start 第二步 并发标记 会出现浮动垃圾
  • CMS-concurrent-preclean-start 第三步 预处理,没有第四步 可中断预处理
  • GC (CMS Final Remark) 第五步 重新标记 会发生STW
  • CMS-concurrent-sweep-start 第六步 并发清理
  • CMS-concurrent-reset-start 第七步 并发重置

CMS GC日志内容分析

[GC (Allocation Failure) [ParNew: 2752K->320K(3072K), 0.0016638 secs] 2752K->916K(9920K), 0.0017058 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
  • ParNew: 2752K->320K(3072K), 0.0016638 secs 新生代 gc前占用2752K,gc回收后320K, 回收了垃圾 2752-320=2432 本次年轻代回收了2432K, 年轻代总大小 3072K,gc回收垃圾耗时0.0016638 secs
  • 2752K->916K(9920K), 0.0017058 secs ,Java堆 gc回收前占用2752K,g回收收获占用916K,Java堆总大小 9920K, 本次堆回收了 2752-916=1836K
  • 该次GC新生代减少了2752-320=2432, Java堆总共减少了2752-916=1836K, 所以 年轻代减少的2432 - 老年代减少的1836=596K 说明该次共有596K内存从年轻代移到了老年代
[GC (CMS Initial Mark) [1 CMS-initial-mark: 3714K(6848K)] 4760K(9920K), 0.0004679 secs]
  1. CMS 初始标记阶段STW, 老年代使用量 3714K,老年代总量6848, java堆使用量4760K, java堆总量9920K
[CMS-concurrent-mark-start]
[CMS-concurrent-mark: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
  1. CMS并发标记阶段,耗时 0.001 secs
[CMS-concurrent-preclean-start]
[CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
  1. 并发预处理阶段,会查找前一段执行过程中,冲新生代升级的对象或者被更新了的对象,预处理重新扫描标记,减少下一个阶段重新标记的工作量
  2. 并发可终止阶段,是为了控制时间,比如扫描多长时间或者eden区比例就终止本阶段
[GC (CMS Final Remark)
[YG occupancy: 1046 K (3072 K)]
[Rescan (parallel) , 0.0001200 secs]
[weak refs processing, 0.0000285 secs][class unloading, 0.0003868 secs]
[scrub symbol table, 0.0004698 secs][scrub string table, 0.0001029 secs]
[1 CMS-remark: 3714K(6848K)] 4760K(9920K), 0.0011706 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs] 
  1. 重新标记阶段/最终标记阶段 STW, 从GC Root开始重新扫描整堆,标记存活的对象。需要注意的是,虽然CMS只回收老年代的垃圾对象,但是这个阶段依然需要扫描新生代,因为GC Root很多都在新生代

    • CMS Final Remark 最终标记阶段
    • YG occupancy: 1046 K (3072 K) 新生代大小为1046K, 新生代总大小3072K
    • Rescan (parallel) , 0.0001200 secs 暂停用户标记情况下,并发的标记对象的过程花费0.0001200 secs
    • weak refs processing, 0.0000285 secs 标记弱引用对象
    • class unloading, 0.0003868 secs 标记已经卸载的类对象
    • scrub symbol table, 0.0004698 secs 标记常量池未被引用的对象
    • scrub string table, 0.0001029 secs 标记常量池String类型未被引用的对象
    • 1 CMS-remark: 3714K(6848K)] 4760K(9920K), 0.0011706 secs 重新标记后 老年代使用3714K,老年代总量6848K, java堆使用量 4760K,java堆总量9920K
[CMS-concurrent-sweep-start]
[CMS-concurrent-sweep: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
  1. 并发清除阶段,清除标记的垃圾对象,花费0.001 secs
[CMS-concurrent-reset-start]
[CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
  1. 并发重置阶段,重置数据和结构信息 及花费的时间

至此 我们讲解了CMS垃圾收集器的配置参数及如何使用CMS垃圾收集器,并且我们通过程序调试JVM参数,配置了CMS垃圾收集器,打印了GC日志,通过对GC日志的分析,能够很好的在实战中了解到底是哪里出了问题,便于JVM调优

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

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

相关文章

图解 | 原来这就是网络

​​ 你是一台电脑&#xff0c;你的名字叫 A 很久很久之前&#xff0c;你不与任何其他电脑相连接&#xff0c;孤苦伶仃。 ​ 直到有一天&#xff0c;你希望与另一台电脑 B 建立通信&#xff0c;于是你们各开了一个网口&#xff0c;用一根网线连接了起来。 ​ 用一根网线连接起来…

[晕事]今天做了件晕事7

今天在使用iptables与grep的时候碰到一件晕事&#xff1b; 第一步添加了一条rule到OUTPUT&#xff1a; iptables -A OUTPUT --source 10.87.51.2 --destination 10.87.51.10 -p tcp --sport 5060 -j DROP 第二步使用&#xff1a;iptables -nL | grep DROP 发现这条记录跑到了FO…

玩转ESP32 PWM输出,制作炫酷呼吸灯效果

文章目录 什么是PWM软硬件使用ESP32实现PWM输出代码讲解结语 什么是PWM PWM&#xff08;Pulse Width Modulation&#xff09;是一种常用的模拟信号产生技术&#xff0c;它通过对一个定时器的计数值进行调整来改变输出信号的占空比&#xff0c;从而控制输出信号的平均电压值&am…

idea使用 ( 二 ) 创建java项目并导入依赖jar

3.创建java项目 3.1.创建普通java项目 3.1.1.打开创建向导 接 2.3.1.创建新的项目 也可以 从菜单选择建立项目 会打开下面的选择界面 3.1.2.不使用模板 3.1.3.设置项目名 Project name : 项目名 Project location : 项目存放的位置 确认创建 3.1.4.关闭tips 将 Dont s…

Spring Boot集成ShardingSphere实现数据分片(一) | Spring Cloud 40

一、背景 传统的将数据集中存储至单一节点的解决方案&#xff0c;在性能、可用性和运维成本这三方面已经难于满足海量数据的场景。 从性能方面来说&#xff0c;由于关系型数据库大多采用 B 树类型的索引&#xff0c;在数据量超过阈值的情况下&#xff0c;索引深度的增加也将使…

Mail 邮件服务

~ Postfix ~ sdskill.com 的邮件发送服务器 ~~ 支持smtps(465)协议连接,使用Rserver颁发的证书,证书路径/CA/cacert.pem ~ 创建邮箱账户“user1~user99”(共99个用户),密码为Chinaskill20!; ~ Dovecot ~ sdskill.com 的邮件接收服务器; ~ 支持imap…

6.微服务项目实战---Sleuth--链路追踪

6.1 链路追踪介绍 在大型系统的微服务化构建中&#xff0c;一个系统被拆分成了许多模块。这些模块负责不同的功能&#xff0c;组合成 系统&#xff0c;最终可以提供丰富的功能。在这种架构中&#xff0c;一次请求往往需要涉及到多个服务。互联网应用构建在不同的软件模块集上…

Docker compose-实现多服务、nginx负载均衡、--scale参数解决端口冲突问题

Docker compose-实现多服务、nginx负载均衡、--scale参数解决端口冲突问题 问题&#xff1a;scale参数端口冲突解决方法&#xff1a;nginx实现多服务、负载均衡修改docker-compose.yml配置新增nginx本地配置文件验证启动容器查看容器状态访问web应用 问题&#xff1a;scale参数…

《二》HTTP 请求报文和响应报文、请求方法、状态码

请求报文和响应报文&#xff1a; 请求报文: 客户端向服务器发送的请求信息&#xff0c;就叫做请求报文。 客户端发送一个 HTTP 请求到服务器&#xff0c;请求信息包含四部分&#xff1a;请求行、请求头、空行、请求体。 请求行&#xff1a;包含三部分&#xff0c;分别是请…

查看库文件是32位还是64位|查看lib是静态库还是导入库|判断是debug模式还是release模式

文章目录 dll位数查看lib位数查看查看lib库是静态库还是导入库dll库文件信息查看lib库文件内容查看dll库查看编译模式是debug还是release方法一方法二方法三 lib静态库查看编译模式是debug还是release方法一方法二 lib导入库查看编译模式是debug还是release查看Linux下的.a库&a…

ROS学习第十五节——常用API(C++)

由于时间问题&#xff0c;从这一节开始只记录C实现效果&#xff0c;加油 以下附上这一节调试用的程序 https://download.csdn.net/download/qq_45685327/87708069 1.初始化函数 void init(int &argc, char **argv, const std::string& name, uint32_t options 0); …

openEuler NFS+协议全新发布:实现NAS存储性能与可靠性倍增

4月21日&#xff0c;在openEuler Developer Day 2023上&#xff0c;openEuler发布NFS协议&#xff0c;实现单客户端访问NAS存储可靠性提升3倍、性能提升6倍&#xff0c;助力NAS存储全面满足新型生产核心场景下苛刻要求。 传统NFS面临挑战 网络文件系统&#xff08;NFS&#xf…

vue打包如何开启gzip压缩

文章目录 场景gzip压缩有两种方案&#xff1a;个人实践 场景 本人前端打包的js达到了6.9M,导致网站加载很慢&#xff0c;想了下可以用gzip的方式压缩&#xff0c;减少文件大小。 “前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c…

什么蓝牙耳机好?测评达人精选五款性价比高蓝牙耳机推荐

用蓝牙耳机听歌、刷视频、玩游戏已经成为趋势&#xff0c;有线耳机已经逐渐被取代&#xff0c;但蓝牙耳机价格跨度大&#xff0c;品牌和型号也非常多&#xff0c;究竟什么蓝牙耳机好&#xff1f;我作为测评员这几年已经体验过五十款蓝牙耳机&#xff0c;今天就来总结五款性价比…

模仿网易云音乐黑胶唱片的交互实现

今天在 .NET MAUI 中我们来实现这个交互效果&#xff0c;先来看看效果&#xff1a; 使用.NET MAU实现跨平台支持&#xff0c;本项目可运行于Android、iOS平台。 创建页面布局 项目模拟了网易云音乐的播放主界面&#xff0c;可播放本地音乐文件。使用MatoMusic.Core作为播放内…

ChatGLM ptuning predict(预测)为空值的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

html学习(布局方式(layout)、浮动(float)、定位(position)、弹性盒(flex))

布局方式(layout) 文档流 文档流&#xff08;normal flow&#xff09; 文档流通俗的讲&#xff0c;就是一个web页面中&#xff0c;每一个模块只能从上到下从左往右的方式排列在页面上。 将窗口自下而上分成一行一行&#xff0c;应在每行中按从左至右的依次排放元素&#xff0…

[ZJCTF 2019]EasyHeap-patchlibc-调试

1,三连 主要功能&#xff1a; 1、malloc申请chunk 2、修改chunk内容 3、free chunk 4、exit 堆题多看一个libc信息&#xff1a; 2,IDA分析 2.1、malloc申请chunk heaparray[i]&#xff1a;存放 chunk 的地址。read_input(heaparray[i], size)&#xff1a;向 chunk 写入 s…

亚马逊云科技宣布四大举措,全方位赋能创新成长企业

4月13日&#xff0c;以“加速创新&#xff0c;成就未来”为主题的亚马逊云科技创新成长中国行深圳论坛圆满举行。会上亚马逊云科技宣布&#xff0c;将围绕创新成长企业的三大业务需求——云上创新、出海拓展、业务加速&#xff0c;提供行业聚焦、技术支撑、伙伴扶持、人才培养四…

ENVI5.3 自动配准流程化工具(Image Registration Workflow)配准方法流程

ENVI5.3 自动配准流程化工具&#xff08;Image Registration Workflow&#xff09;配准方法流程。 打开 ENVI软件中的Image Registration Workflow工具&#xff0c;分辨打开参考图像和待校正的图像。 Base Image File是参考图像&#xff0c;参考图像的范围应比待校正图像的范围…