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

垃圾收集器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.xdnf.cn/news/16824.html

相关文章:

  • 中州养老Day02:服务管理护理计划模块
  • 全球化 2.0 | 中国香港教育机构通过云轴科技ZStack实现VMware替代
  • 离线安装docker和docker-compose
  • UNet改进(28):KD Attention增强UNet的知识蒸馏方法详解
  • 【龙芯99派新世界】buildroot快速使用笔记
  • Makefile 入门与实践指南
  • 易华路副总经理兼交付管理中心部门经理于江平受邀PMO大会主持人
  • SQL Server从入门到项目实践(超值版)读书笔记 22
  • 5.7 ASPICE适配过程中的认证准备
  • K8S的Pod之initC容器restartPolicy新特性
  • .NET 中,Process.Responding 属性用于检查进程的用户界面是否正在响应
  • 《React+TypeScript实战:前端状态管理的安全架构与性能优化深解》
  • 音频3A处理简介之AGC(自动增益控制)
  • Python从入门到精通计划Day01: Python开发环境搭建指南:从零开始打造你的“数字厨房“
  • 北京-4年功能测试2年空窗-报培训班学测开-今天来聊聊我的痛苦
  • 防火墙配置实验2(DHCP,用户认证,安全策略)
  • Python 入门指南:从零基础到环境搭建
  • Windows 批处理(.bat)文件中,搜索文件时使用的通配符
  • 排序算法大全:从插入到快速排序
  • EPICS aSub记录示例2
  • 计算机网络:任播和负载均衡的区别
  • 【Linux系统】详解,进程控制
  • Flink2.0学习笔记:Stream API 窗口
  • 20250802让飞凌OK3576-C开发板在飞凌的Android14下【rk3576_u选项】适配NXP的WIFIBT模块88W8987A的蓝牙
  • 【深度学习新浪潮】什么是专业科研智能体?
  • python:如何调节机器学习算法的鲁棒性,以支持向量机SVM为例,让伙伴们看的更明白
  • Kubernetes 构建高可用、高性能 Redis 集群实战指南
  • AI应用标准详解:A2A MCP AG-UI
  • MySQL 运算符
  • WebForms 简介