4. 垃圾收集器ParNewCMS底层三色标记算法详解

news2024/11/16 8:18:53

JVM性能调优

  • 1. 垃圾收集算法
    • 1.1 分代收集理论
    • 1.2 标记-复制算法
    • 1.3 标记-清除算法
    • 1.4 标记-整理算法
  • 2. 垃圾收集器
    • 2.1 Serial收集器(-XX:+UseSerialGC -XX:+UseSerialOldGC)
    • 2.2 Parallel Scavenge收集器(-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代))
    • 2.3 ParNew收集器(-XX:+UseParNewGC)
    • 2.4 CMS收集器(-XX:+UseConcMarkSweepGC(old))

本文是按照自己的理解进行笔记总结,如有不正确的地方,还望大佬多多指点纠正,勿喷。

课程内容:
1、垃圾收集算法详解

2、垃圾收集器Serial&Parallel&ParNew&CMS详解

3、亿级流量电商系统如何设置合适的垃圾收集器

4、垃圾收集底层三色标记算法实现原理

5、CMS并发清理阶段会误删新生成的对象吗

6、从Hotspot源码级别讲透读写屏障彻底理解三色标记

1. 垃圾收集算法

  • 分代收集理论

  • 复制算法

  • 标记整理算法

  • 标记清除算法

1.1 分代收集理论

当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将java堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。

比如在新生代中,每次收集都会有大量对象(近99%)死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择'标记-清除"或"标记-整理"算法进行垃圾收集

注意,“标记-清除"或“标记-整理"算法会比复制算法慢10倍以上。

1.2 标记-复制算法

为了解决效率问题,“复制"收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。

就是把存活的对象进行标记,也有极少的进行标记垃圾对象。

在这里插入图片描述

1.3 标记-清除算法

算法分为"标记"和"清除"阶段:标记存活的对象统一回收所有未被标记的对象(一般选择这种);也可以反过来,标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。它是最基础的收集算法,比较简单,但是会带来两个明显的问题:

  1. 效率问题(如果需要标记的对象太多,效率不高)
  2. 空间问题(标记清除后会产生大量不连续的碎片)

在这里插入图片描述
我们年轻代可以使用复制算法,但是老年代并没有使用该算法,原因就是浪费空间。因为比如我们原来又1G的内存,我们久只可以用500G的内存。老年代就不一样了,他大部分是存活的,不可能空出一半的内容让你实现复制算法。

老年代一般都是使用标记-清除、标记-整理算法。先把存活对象标记出来,然后把没有标记的全部干掉。

1.4 标记-整理算法

根据老年代的特点特出的一种标记算法,标记过程仍然与"标记-清除"算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。

在这里插入图片描述

2. 垃圾收集器

在这里插入图片描述

如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。

虽然我们对各个收集器进行比较,但并非为了挑选出一个最好的收集器。因为直到现在为止还没有最好的垃圾收集器出现,更加没有万能的垃圾收集器,我们能做的就是根据具体应用场景选择适合自己的垃圾收集器。

试想一下:如果有一种四海之内、任何场景下都适用的完美收集器存在,那么我们的Java虚拟机就不会实现那么多不同的垃圾收集器了。

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

Serial (串行)收集器是最基本、历史最悠久的垃圾收集器了。大家看名字就知道这个收集器是一个单线程收集器了。它的“单线程”的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程(“Stop The World” ) ,直到它收集结束。

新生代采用复制算法,老年代采用标记-整理算法。

在这里插入图片描述

虚拟机的设计者们当然知道Stop The World带来的不良用户体验,所以在后续的垃圾收集器设计中停顿时间在不断缩短(仍然还有停顿,寻找最优秀的垃圾收集器的过程仍然在继续)。

但是Serla收集器有没有优于其他垃圾收集器的地方呢?当然有,它简单而高效(与其他收集器的单线程相比)。Serial收集器由于没有线程交互的开销,自然可以获得很高的单线程收集效率。

Serial Old收集器是Seria收集器的老年代版本,它同样是一个单线程收集器。它主要有两大用途:一种用途是在JDK1.5以及以前的版本中与Parallel Scavenge收集器搭配使用,另一种用途是作为CMS收集器的后备方案。

2.2 Parallel Scavenge收集器(-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代))

Paralle收集器其实就是Serial收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和Serial收集器类似。默认的收集线程数跟cpu核数相同,当然也可以用参数(-XX:ParallelGCThreads)指定收集线程数,但是一般不推荐修改。

Parallel Scavenge收集器关注点是吞吐量(高效率的利用CPU)。CMS等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。所谓吞吐量就是CPU中用于运行用户代码的时间与CPU总消耗时间的比值。Parallel Scavenge收集器提供了很多参数供用户找到最合适的停顿时间或最大吞吐量,如果对于收集器运作不太了解的话,可以选择把内存管理优化交给虚拟机去完成也是一个不错的选择。

新生代采用复制算法,老年代采用标记-整理算法。

在这里插入图片描述

Parallel Old收集器是Parallel Scavenge收集器的老年代版本。使用多线程和标记-整理"算法。在注重吞吐量以及CPU资源的场合,都可以优先考虑Parallel Scavenge收集器和Parallel Old收集器(JDK8默认的新生代和老年代收集器)。

2.3 ParNew收集器(-XX:+UseParNewGC)

ParNew收集器其实跟Parallel收集器很类似,区别主要在于它可以和CMS收集器配合使用。

新生代采用复制算法,老年代采用标记-整理算法。

在这里插入图片描述
它是许多运行在Server模式下的虚拟机的首要选择,除了Serial收集器外,只有它能与CMS收集器(真正意义上的并发收集器,后面会介绍到) 配合工作。

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

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

从名字中的Mark Sweep这两个词可以看出,CMS收集器是一种“标记-清除"算法实现的,它的运作过程相比于前面几种垃圾收集器来说更加复杂一些。

整个过程分为四个步骤:

  • 初始标记: 暂停所有的其他线程(STW),并记录下gc roots直接能引用的对象,速度很快。(如果不STW一直在运行就会一直有局部变量引用新的对象,那这个初始标记一直做不完)

  • 并发标记: 并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行。因为用户程序继续运行,可能会有导致已经标记过的对象状态发生改变。

  • 重新标记: 重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。主要用到三色标记里的增量更新算法(见下面详解)做重新标记。

  • 并发清理: 开启用户线程,同时GC线程开始对未标记的区域做清扫。这个阶段如果有新增对象会被标记为黑色不做任何处理(见下面三色标记算法详解)。(我们标记的是非垃圾对象,凡是没被标记的对象都应该清除掉)

  • 并发重置: 重置本次GC过程中的标记数据。

在这里插入图片描述

从它的名字就可以看出它是一款优秀的垃圾收集器,主要优点∶并发收集、低停顿。但是它有下面几个明显的缺点:

  • 对CPU资源敏感(会和服务抢资源);

  • 无法处理浮动垃圾(在并发标记和并发清理阶段又产生垃圾,这种浮动垃圾只能等到下一次gc再清理了);

  • 它使用的回收算法“标记-清除"算法会导致收集结束时会有大量空间碎片产生,当然通过参数-XX:+UseCMSCompactAtFullCoection可以让jvm在执行完标记清除后再做整理

  • 执行过程中的不确定性,会存在上一次垃圾回收还没执行完然后垃圾回收又被触发的情况,特别是在并发标记和并发清理阶段会出现,一边回收,系统一边运行,也许没回收完就再次触发full gc,也就是"concurrent mode failure",此时会进入stop the world,用serial old垃圾收集器来回收

CMS的相关核心参数

  1. -XX:+UseConcMarkSweepGC: 启用cms

  2. -XX:ConcGCThreads:并发的GC线程数

  3. -XX:+UseCMSCompactAtFullCollection: FullGC之后做压缩整理(减少碎片)

  4. -XX:CMSFullGCsBeforeCompaction: 多少次FullGC之后压缩一次,默认是O,代表每次FullGC后都会压缩一次

  5. -XX:CMSInitiatingOccupancyFraction:当老年代使用达到该比例时会触发FullGC(默认是92,这是百分比)

  6. -XX.:+UseCMSInitatingOccupancyOnly: 只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设定的值),如果不指定,JVM仅在第一次使用设定值,后续则会自动调整

  7. -XX:+CMSScavengeBeforeRemark:在CMS GC前启动一次minor gc,目的在于减少老年代对年轻代的引用,降低CMS GC的标记阶段时的开销,一般CMS的GC耗时80%都在标记阶段

  8. -XX:+CMSParallellnitialMarkEnabled:表示在初始标记的时候多线程执行,缩短STW9.-XX:+CMSParallelRemarkEnabled:在重新标记的时候多线程执行,缩短STW;

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

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

相关文章

浅谈MySQL主键

常用主键 常用主键 1)自增 int、bigint等,顺序递增。 2)雪花 雪花算法是因为有时间参数,所以是有序地,而且都是由数字组成。雪花id最大为64位,符合java中long的长度64位,适用于大规模分布式场景。 3&#…

docker基础操作与进阶 - 搭建基于pm2的node环境

1、为什么要使用docker 最近遇到一台机器需要部署两个不同版本node的情况,首先就想起了docker,想必还有其他类似环境问题的情况,需要进行项目隔离,而docker正是用来解决这个问题的。 docker的优势就在于环境隔离,相当…

第九篇、基于Arduino uno,用LCD1602(带IIC的)显示屏显示字符——结果导向

0、结果 说明:可以在LCD1602屏幕上面显示字符,实时的变量,也可以设置是否背光,如果是你想要的,可以接着往下看。 1、外观 说明:注意是带IIC通讯的LCD屏幕,外形如下。 2、连线 说明&#xff…

Hexo写文章不方便?用上GitHub Actions真的是泰裤辣

对于做个人博客的小伙伴来说 HEXO 大家肯定都是非常熟悉的吧,这是一个静态的个人博客程序,通过 HEXO + GitHub Pages 搭建免费个人博客也是很多博主的选择。但相信肯定也会有些困恼,比如博客的渲染维护太麻烦了,我要在一台新设备上写博客并推送到 GitHub Pages 还要先安装 …

【Redis】聊一下哨兵集群

上一篇中,介绍了哨兵机制可以减少主库实例下线的误判率,但是如果只有一个哨兵实例,出现宕机后没有办法保证服务的高可用,所以一般实际的生产环境都是搭建3个哨兵实例构建的集群进行运行。但是具体的运行机制是什么。其实主要就是三…

使用langchain及llama_index实现基于文档(长文本)的相似查询与询问

文章目录 1. 引言2. 简介3. 带关键字的查询方案4. 不带关键字的总结询问5. 实现代码 1. 引言 在调用ChatGPT接口时,我们常常受到4096个字符(token)的限制。这种限制对于处理长文本或者需要对文档进行相似查询和询问的场景来说是一个挑战。然…

如何复制投票链接投票怎样链接到公众号投票链接如何生成

关于微信投票,我们现在用的最多的就是小程序投票,今天的网络投票,在这里会教大家如何用“活动星投票”小程序来进行投票。 我们现在要以“妙趣拾光”为主题进行一次投票活动,我们可以在在微信小程序搜索,“活动星投票”…

【Python开发】FastAPI 03:请求参数—请求体

除了路径参数和查询参数,还有请求体,其用于传递 JSON、XML 或其他格式的数据,以便服务器能够读取并做出相应的处理,可以说请求体的作用更为强大。试想一下,如果存在七八个参数,路径参数和查询是不是就招架不…

Camera | 10.linux驱动 led架构-基于rk3568

前面文章我们简单给大家介绍了如何移植闪光灯芯片sgm3141,该驱动依赖了led子系统和v4l2子系统。 V4L2可以参考前面camera系列文章,本文主要讲述led子系统。 一、LED子系统框架 Linux内核的 led 子系统主要功能: 为每个设备在/sys/class/le…

《Linux内核源码分析》(2)进程原理及系统调用

《Linux内核源码分析》(2)进程原理及系统调用 一、进程 操作系统的作用:作为硬件的使用层,提供使用硬件资源的能力, 进程的作用:作为操作系统使用层,提供使用操作系统抽象出的资源层的能力 进程、线程和程序的区别&…

【计算机视觉】Segment Anything 安装配置及代码测试(含源代码)

文章目录 一、前言二、安装2.1 基本要求2.2 Install Segment Anything 三、代码使用示例3.1 Automatically generating object masks with SAM3.2 Environment Set-up3.3 显示标注3.4 图像示例3.5 Automatic mask generation3.6 Automatic mask generation options 一、前言 目…

客户体验:响应速度是他们的 No.1 Pick么?

服务响应速度在为消费者提供服务时极为重要,那么,在消费者整体体验中,响应速度是否是消费者最在意的呢? 无论是对企业还是消费者来说,时间都至关重要。消费者在寻求客户服务时,不喜欢等待。根据《客户服务受…

【Python asyncio】零基础也能轻松掌握的学习路线与参考资料

Python asyncio是一个强大而易于使用的库,让Python程序员能够编写高效的异步IO应用程序。它为程序员提供了一种简单而优雅的方法来避免使用 Python GIL(全局解释器锁),同时允许他们轻松地处理高并发的网络通信和并发任务执行。下面…

蓝桥杯数论总结:快速幂和矩阵快速幂

本文先是给出快速幂的原理,又由一道例题明确快速幂的Python代码模版;而后给出矩阵快速幂的原理(介绍了矩阵相乘,对没学过线代者友好),和矩阵快速幂的模版。再给出快速幂和矩阵快速幂相关的题单。 目录 快…

Linux高级---k8s存储

文章目录 一、数据卷的概述二、关系图三、数据卷的类型1、emptydira、描述b、适用场景c、emptydir应用 2、hostpatha、描述b、适用场景c、hostpath应用 3、nfsa、描述b、适用场景c、nfs应用 4、PV和PVCa、描述b、存储卷和存储卷声明的关系c、存储卷声明的管理过程 5、PVa、资源…

10个可以快速用Python进行数据分析的小技巧

一些小提示和小技巧可能是非常有用的,特别是在编程领域。有时候使用一点点黑客技术,既可以节省时间,还可能挽救“生命”。 一个小小的快捷方式或附加组件有时真是天赐之物,并且可以成为真正的生产力助推器。所以,这里…

基于Spring Boot+Vue的课堂管理系统

介绍 基于Spring BootVue的课堂管理系统。前后端分离。包含教师授课管理、学生选退课、聊天室、签到、笔记管理模块等。 技术架构 spring BootMyBatisRedisWebSocketVueCLIAxiosElement UI 项目特点: - 后台使用MyBatis连接数据库,编写后台服务器的…

【教程】2步白嫖使用DeepL Pro会员版 [附插件]

转载请注明出处:小锋学长生活大爆炸[xfxuezhang.cn] 【教程】5步白嫖使用Grammarly Premium高级版 [附脚本] 适用说明 Edge、Chrome等,使用了Chrome内核的浏览器应该都可以吧。 开始白嫖 1、下载并解压插件,下载链接:https://x…

第2章:数据结构【AcWing】

文章目录 单链表定义初始化头插在下标为pos位置后插入删除下标为pos后的结点遍历 双链表定义初始化在下标为pos后插入删除下标为pos的位置 栈和队列栈定义示例代码 队列定义示例代码循环队列定义示例代码 单调栈和单调队列单调栈朴素方法 O ( n 2 ) O(n^2) O(n2)优化 O ( n ) …

Web的基本漏洞--逻辑漏洞

目录 一、逻辑漏洞介绍 1.逻辑漏洞的原理 2.逻辑漏洞的分类 3.常见的逻辑漏洞 4.挖掘逻辑漏洞 一、逻辑漏洞介绍 1.逻辑漏洞的原理 逻辑漏洞是指由于程序逻辑不严或逻辑太复杂,导致一些逻辑分支不能够正常处理或处理错误,从而进行攻击。一般出现任…