2026/4/18 13:00:35
网站建设
项目流程
求网站建设的视频教程,产品开发流程图,河北省建设项目环保备案网站,宁波关键词排名优化在 JVM 垃圾收集器#xff08;尤其是分代收集器、G1/CMS 等并发收集器#xff09;的实现中#xff0c;记忆集#xff08;Remembered Set#xff0c;RS#xff09; 和读写屏障#xff08;Read/Write Barrier#xff09; 是解决跨代引用追踪和并发标记一致性的核心技术。…在 JVM 垃圾收集器尤其是分代收集器、G1/CMS 等并发收集器的实现中记忆集Remembered SetRS和读写屏障Read/Write Barrier是解决跨代引用追踪和并发标记一致性的核心技术。二者相互配合既避免了全堆扫描的性能损耗又保证了并发场景下垃圾标记的准确性是理解现代 GC 机制的关键。一、记忆集Remembered Set解决跨代引用的 “索引”1. 为什么需要记忆集分代垃圾收集的核心假设是新生代对象朝生夕死老年代对象存活时间长因此新生代 GCMinor GC的频率远高于老年代 GCMajor GC。但分代模型存在一个关键问题老年代对象可能引用新生代对象跨代引用。根据可达性分析算法新生代 GC 时需要扫描GC Roots而老年代的跨代引用属于 GC Roots 的一部分。如果每次 Minor GC 都全量扫描老年代会导致 STW 时间急剧增加完全违背分代收集的初衷。记忆集的核心作用记录老年代对象指向新生代对象的引用或跨 Region 引用使 GC 时只需扫描记忆集而非全量扫描老年代 / 其他 Region从而大幅减少扫描范围。2. 记忆集的定义与本质记忆集是一种辅助数据结构本质是 **“从非收集区到收集区的引用的集合”**如老年代→新生代、Old Region→Eden Region。它为每个收集区如新生代的 Eden/Survivor维护一个索引记录哪些非收集区的内存块中存在指向该收集区的引用。3. 记忆集的实现粒度记忆集的实现粒度决定了 GC 扫描的效率和内存开销常见的粒度从粗到细分为三级粒度级别描述优点缺点卡表Card Table将堆内存划分为大小固定的卡页Card Page通常 512 字节用一个字节数组表示卡表每个元素对应一个卡页标记该卡页是否存在跨代引用。实现简单、内存开销低堆内存的 1/512、操作高效粒度较粗可能扫描少量无关对象段表Segment Table按内存段如更大的内存块划分记录段级别的跨代引用。平衡粒度与开销适用场景有限不如卡表常用精确表Precise Table记录具体的对象引用地址粒度精确到单个对象。扫描无冗余效率最高内存开销大、更新成本高最主流的实现卡表Card TableJVM 中几乎所有收集器CMS、G1、ParNew 等都采用卡表作为记忆集的实现以 HotSpot 为例将堆内存按512 字节划分为一个个卡页用一个unsigned char类型的数组卡表对应所有卡页数组下标与卡页的内存地址一一映射当某个卡页中存在老年代对象指向新生代对象的引用时将卡表中对应位置的字节标记为脏Dirty如 0x01新生代 GC 时只需扫描卡表中标记为 “脏” 的卡页即可找到所有跨代引用无需扫描整个老年代。4. 记忆集的适用场景分代收集器ParNew CMS、Parallel Scavenge Parallel Old 等用于记录老年代→新生代的跨代引用G1 收集器用于记录不同 Region 之间的引用如 Old Region→Eden Region、Old Region→Other Old Region因为 G1 的堆是由多个独立 Region 组成的跨 Region 引用同样需要索引ZGC/Shenandoah虽采用整堆收集但仍需记忆集辅助追踪跨区域引用。5. 记忆集的更新时机记忆集的准确性依赖于引用变化时的及时更新而这个更新操作正是由写屏障触发的下文详细讲解。当对象的引用字段被修改时如老年代对象的字段指向新生代对象写屏障会触发卡表的 “脏标记” 操作确保记忆集能反映最新的引用关系。二、读写屏障Read/Write Barrier拦截对象访问的 “钩子”1. 屏障的本质在 JVM 中屏障Barrier是在对象的读、写操作前后插入的一段额外代码用于拦截并处理特定逻辑如更新记忆集、维护并发标记的一致性。它类似于 AOP 的切面在不修改业务代码的前提下为对象访问操作增加额外的 “副作用”。根据拦截的操作类型分为读屏障Read Barrier和写屏障Write Barrier其中写屏障是 GC 中最常用的读屏障仅在少数收集器如 ZGC、Shenandoah中使用。2. 写屏障Write BarrierGC 的核心 “触发器”写屏障拦截的是对象引用字段的赋值操作如obj.field newObj是实现记忆集更新、并发标记一致性的核心机制。1写屏障的主要作用更新记忆集卡表脏标记当修改对象引用导致跨代 / 跨 Region 引用产生时触发卡表的 “脏标记”维护记忆集的准确性维护并发标记的一致性在 CMS、G1 等并发收集器中写屏障用于记录引用的变化解决并发标记时的 “标记遗漏” 问题如增量更新、SATB 算法辅助其他 GC 机制如在 G1 中记录 Humongous 对象的引用变化在 ZGC 中维护颜色指针的一致性。2写屏障的实现方式HotSpot 中的写屏障分为预写屏障Pre-Write Barrier和后写屏障Post-Write Barrier分别在赋值操作前后执行// 原始赋值操作obj.field newObj obj.field newObj; // 插入写屏障后的逻辑 pre_write_barrier(obj, field, newObj); // 预写屏障赋值前执行 obj.field newObj; // 原始赋值 post_write_barrier(obj, field, oldObj); // 后写屏障赋值后执行核心场景 1卡表的脏标记后写屏障当老年代对象的引用字段被修改为指向新生代对象时后写屏障会触发以下操作计算该老年代对象所在的卡页地址将卡表中对应卡页的标记设为 “脏”这个过程由 JVM 底层自动完成无需开发者干预。例如 HotSpot 中的card_table::make_dirty函数就是实现脏标记的核心逻辑。核心场景 2并发标记的一致性保障在并发标记阶段用户线程和 GC 线程并行执行对象引用可能被修改导致标记遗漏。写屏障通过以下两种算法解决该问题增量更新Incremental UpdateCMS 收集器采用当对象的引用被修改时写屏障将被修改的对象标记为 “需要重新标记”确保在重新标记阶段Remark能处理这些对象避免遗漏SATBSnapshot At The Beginning初始快照G1 收集器采用当对象的引用被修改时写屏障将旧的引用对象记录到日志中确保并发标记基于 “标记开始时的对象图快照”避免遗漏。3写屏障的性能开销写屏障会在每次对象引用赋值时插入额外代码理论上存在性能开销但实际影响极小HotSpot 对写屏障做了大量优化如内联、编译器优化使其开销降低到纳秒级别写屏障的开销远小于其带来的 GC 性能提升如避免全堆扫描。3. 读屏障Read Barrier少数收集器的 “特殊工具”读屏障拦截的是对象引用的读取操作如Object obj obj.field由于读取操作的频率远高于写入操作读屏障的开销更大因此仅在追求极致低延迟的收集器中使用。1读屏障的适用场景ZGC/Shenandoah 收集器用于实现颜色指针Colored Pointer机制在读取对象引用时检查指针的颜色标记判断对象是否被移动或需要重新定位从而实现几乎无 STW 的并发收集低延迟场景在并发整理阶段读屏障可确保线程读取到的是对象的最新地址避免访问已被移动的对象。2读屏障的实现示例以 ZGC 为例当线程读取对象引用时读屏障会检查指针的标记位如果指针标记为 “已移动”则通过指针的元数据找到对象的新地址返回新地址如果指针正常则直接返回原地址。这种机制使 ZGC 能在并发整理阶段移动对象而无需暂停用户线程。三、记忆集与读写屏障的协同工作流程以ParNew CMS的新生代 GCMinor GC为例二者的协同过程如下对象赋值阶段当老年代对象的字段指向新生代对象时写屏障触发卡表的脏标记将该老年代对象所在的卡页标记为 “脏”Minor GC 触发当新生代 Eden 区满时触发 Minor GCGC Roots 扫描扫描线程栈、全局静态变量等传统 GC Roots扫描记忆集卡表中标记为 “脏” 的卡页找到所有老年代→新生代的跨代引用作为扩展的 GC Roots可达性分析基于上述 GC Roots遍历新生代对象图标记存活对象垃圾回收回收新生代的死亡对象将存活对象复制到 Survivor 区或老年代卡表清理GC 完成后清理卡表中的脏标记为下一次 GC 做准备。再以G1 的并发标记阶段为例初始标记STW标记 GC Roots 直接关联的对象写屏障开始记录引用变化并发标记GC 线程遍历对象图用户线程并行执行写屏障通过 SATB 算法记录旧引用到日志中最终标记STW处理写屏障记录的日志修正标记结果筛选回收根据记忆集记录的跨 Region 引用优先回收垃圾比例最高的 Region同时通过写屏障维护记忆集的准确性。四、关键总结组件核心作用实现方式典型使用场景记忆集卡表记录跨代 / 跨 Region 引用避免全堆扫描卡表主流、段表、精确表CMS、G1、ParNew 等几乎所有现代收集器写屏障触发记忆集更新、维护并发标记一致性预写屏障、后写屏障增量更新、SATB所有收集器的记忆集维护CMS/G1 的并发标记读屏障实现无 STW 的并发收集、颜色指针机制读取引用时检查指针状态ZGC、Shenandoah核心结论记忆集是空间换时间的典型设计通过维护引用索引大幅减少 GC 的扫描范围读写屏障是逻辑插桩的实现为 GC 提供了引用变化的 “感知能力”是并发收集的基础二者的配合是现代 GC 能在高并发、大内存场景下保证性能的关键也是理解 G1、ZGC 等先进收集器的核心。对于开发者而言无需手动操作这些机制但理解其原理有助于更好地调优 GC 参数、排查 GC 相关的性能问题。