当前位置: 首页 > news >正文

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

垃圾收集技术详解笔记

1. 分代收集理论

当前虚拟机的垃圾收集采用分代收集算法,根据对象存活周期将内存分为不同代区,以优化回收效率。

  • 核心分区
    • 新生代(Young Generation):对象存活周期短,约99%对象在每次回收时死亡。
    • 老年代(Old Generation):对象存活率高,无额外分配担保空间。
  • 算法选择
    • 新生代:适用复制算法(效率高,只需复制少量存活对象)。
    • 老年代:适用标记-清除或标记-整理算法(避免复制开销,但速度慢10倍以上)。
2. 垃圾收集算法详解
2.1 标记-复制算法

解决效率问题,将内存分为大小相等的两块。

  • 工作流程
    1. 使用其中一块内存。
    2. 当内存耗尽,将存活对象复制到另一块。
    3. 清理原内存块。
  • 内存变化示例
    • 整理前:碎片化状态。
    • 整理后:存活对象集中到另一块,内存连续。
  • 内存区域类型
    • 可用内存、可回收内存、存活对象、保留内存。
2.2 标记-清除算法

最基础算法,分“标记”和“清除”两阶段。

  • 工作流程
    • 标记存活对象(或需回收对象)。
    • 清除未标记对象。
  • 缺点
    • 效率问题:标记大量对象时性能低。
    • 空间问题:产生内存碎片(不连续空间)。
  • 内存变化示例
    • 整理前:碎片化状态。
    • 整理后:碎片化更严重,仅区分可用内存、可回收内存、存活对象。
2.3 标记-整理算法

专为老年代设计,标记后移动对象以消除碎片。

  • 工作流程
    1. 标记存活对象。
    2. 将所有存活对象向一端移动。
    3. 清理边界外内存。
  • 内存变化示例
    • 回收前:碎片化状态。
    • 回收后:对象紧凑排列,区分存活对象、可回收内存、未使用内存。
3. 垃圾收集器实现

收集器是算法的具体实现,需根据场景选择。无“万能”收集器。

3.1 Serial收集器(-XX:+UseSerialGC, -XX:+UseSerialOldGC)
  • 特点
    • 单线程收集器,工作时暂停所有线程(Stop The World)。
    • 简单高效(无线程交互开销)。
  • 算法
    • 新生代:复制算法。
    • 老年代:标记-整理算法。
  • 适用场景:客户端模式或小内存应用。
3.2 Parallel Scavenge收集器(-XX:+UseParallelGC, -XX:+UseParallelOldGC)
  • 特点
    • Serial的多线程版本(默认线程数 = CPU核数)。
    • 关注吞吐量(CPU运行用户代码时间占比)。
  • 算法
    • 新生代:复制算法。
    • 老年代:标记-整理算法。
  • Parallel Old收集器
    • Parallel Scavenge的老年代版本,多线程 + 标记-整理算法。
    • JDK8默认收集器,适合高吞吐场景。
3.3 ParNew收集器(-XX:+UseParNewGC)
  • 特点
    • 类似Parallel,但专为与CMS收集器配合设计。
    • 新生代使用复制算法。
  • 适用场景:Server模式下的首选(与CMS兼容)。
3.4 CMS收集器(-XX:+UseConcMarkSweepGC)
  • 特点
    • 并发收集器(最短停顿时间),用户线程与GC线程并行。
    • 基于标记-清除算法。
  • 工作流程
    1. 初始标记(STW):标记GC Roots直接引用对象。
    2. 并发标记:遍历对象图(无停顿)。
    3. 重新标记(STW):修正并发标记变动(用增量更新算法)。
    4. 并发清理:清除未标记对象。
    5. 并发重置:重置标记数据。
  • 缺点
    • CPU敏感(与业务线程抢资源)。
    • 浮动垃圾(并发阶段新垃圾需下次回收)。
    • 内存碎片(需定期整理)。
    • 不确定性(可能触发Concurrent Mode Failure,降级为Serial Old)。
  • 关键参数
    -XX:+UseConcMarkSweepGC     # 启用CMS  
    -XX:ConcGCThreads            # 并发GC线程数  
    -XX:+UseCMSCompactAtFullCollection  # FullGC后整理碎片  
    -XX:CMSFullGCsBeforeCompaction       # 多少次FullGC后整理一次(默认0)  
    -XX:CMSInitiatingOccupancyFraction   # 老年代使用比例触发FullGC(默认92%)  
    -XX:+CMSScavengeBeforeRemark         # CMS前启动Minor GC  
    
4. 亿级流量电商系统JVM优化案例

针对订单系统(8G内存,分配4G给JVM)。

  • 优化目标:减少Full GC(避免对象过早进入老年代)。
  • 参数配置
    -Xms3072M -Xmx3072M -Xmn2048M   # 堆大小(新生代2G)  
    -Xss1M                          # 线程栈大小  
    -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M  # 元空间  
    -XX:SurvivorRatio=8             # Eden与Survivor比例  
    -XX:MaxTenuringThreshold=5      # 对象年龄阈值(从15改为5)  
    -XX:PretenureSizeThreshold=1M   # 直接进入老年代对象大小阈值  
    -XX:+UseParNewGC -XX:+UseConcMarkSweepGC  # 新生代ParNew + 老年代CMS  
    -XX:CMSInitiatingOccupancyFraction=92     # 老年代92%触发FullGC  
    -XX:+UseCMSCompactAtFullCollection        # FullGC后整理碎片  
    -XX:CMSFullGCsBeforeCompaction=3          # 每3次FullGC整理一次  
    
  • 优化原理
    • 增大新生代(-Xmn2048M),减少对象动态年龄判断导致的过早晋升。
    • 降低对象年龄阈值(-XX:MaxTenuringThreshold=5),确保短期对象在Minor GC回收。
    • CMS默认参数适合高峰后Full GC(几小时一次)。
5. 垃圾收集底层算法
5.1 三色标记算法

解决并发标记中的漏标问题,将对象分为三色:

  • 黑色:已扫描完(安全存活)。
  • 灰色:已扫描,但引用未全扫描。
  • 白色:未扫描(不可达对象)。
  • 问题与解决
    • 多标(浮动垃圾):并发阶段局部变量销毁导致本应回收的对象未被回收(下一轮GC处理)。
    • 漏标:通过读写屏障解决:
      • 增量更新(CMS使用):黑色对象插入新引用时记录,重新扫描。
      • 原始快照(SATB, G1使用):灰色对象删除引用时记录,重新扫描。
  • 读写屏障实现
    // 写屏障示例(增量更新)  
    void oop_field_store(oop* field, oop new_value) {pre_write_barrier(field);    // 写前操作(记录旧值)*field = new_value;post_write_barrier(field, new_value); // 写后操作(记录新值)
    }
    
5.2 记忆集与卡表

解决跨代引用问题(如新生代引用老年代)。

  • 记忆集(Remember Set):记录跨代指针集合。
  • 卡表实现:字节数组(CARD_TABLE[]),每卡页512字节。
    • 卡页变脏(=1):有跨代指针时更新。
  • 维护:通过写屏障自动更新卡表状态。
6. 总结
  • 分代收集是JVM垃圾回收核心,新生代和老年代需匹配不同算法。
  • 收集器选择需权衡吞吐量(Parallel Scavenge)和停顿时间(CMS)。
  • 优化案例显示:合理配置新生代大小、对象年龄阈值及CMS参数可显著减少Full GC。
  • 底层算法(三色标记、读写屏障)确保并发标记的正确性,避免漏标。

http://www.dtcms.com/a/311338.html

相关文章:

  • Redisson高并发实战:Netty IO线程免遭阻塞的守护指南
  • JVM 03 类加载机制
  • uniapp scroll-view解析
  • 常用git命令
  • 算法训练营DAY46 第九章 动态规划part13
  • 【龙芯99派新世界】buildroot快速使用笔记
  • SPI通信中CS片选的两种实现方案:硬件片选与软件片选
  • 电力系统分析学习笔记(二)- 标幺值计算与变压器建模
  • QT5.12.8 QTabWidget 透明样式QSS
  • Flask + YARA-Python*实现文件扫描功能
  • C++音视频开发:基础面试题
  • STM32 GPIO 中8种配置模式
  • 图漾AGV行业常用相机使用文档
  • 快速了解机器学习
  • 【机器学习】非线性分类算法详解(下):决策树(最佳分裂特征选择的艺术)与支持向量机(最大间隔和核技巧)
  • Python Pandas.unique函数解析与实战教程
  • mac中使用gvm install没有效果
  • 【Android】进度条ProgressBar 可拖拽进度条Seekbar
  • 云运维解决方案(word)
  • Python 入门指南:从零基础到环境搭建
  • 数字化转型-灯塔工厂建设
  • StyleX:Meta推出的高性能零运行时CSS-in-JS解决方案
  • 数字图像处理(冈萨雷斯)第三版:第一章绪论主要内容和重点——(数字图像处理的一些概念)
  • C语言数据结构(4)单链表专题2.单链表的应用
  • 什么是需量跟随
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现道路上头盔的检测识别(C#代码,UI界面版)
  • Linux 嵌入式开发全流程
  • C语言(长期更新)第7讲:VS实用调试技巧
  • LVGL + ESP-Brookesia 在Windows下的编译和运行
  • Elasticsearch+Logstash+Filebeat+Kibana单机部署