发布时间:2026/8/14 22:38:56
JVM G1 回收器 RSet 与 PRT 深度解析 源码版本OpenJDK 8u HotSpotsrc/share/vm/gc_implementation/g1/关键词G1、Remembered Set、Per Region Table、写屏障、Card Table、粗化Coarsening1. 背景为什么 G1 需要 RSetG1Garbage-First是一款以Region为回收基本单位、追求可预测停顿的低延迟收集器。它把堆切成大量大小相等的HeapRegion默认约 2048 个每个 1MB~32MB回收时不再像传统分代收集器那样整代扫描而是只挑选若干region组成Collection SetCSet进行增量回收。这就带来一个核心难题增量回收某个 region 时如何在不扫描整个堆的前提下找到谁还在引用我如果 A region 里的对象被 B、C、D 等多个 region 引用回收 A 时就必须扫描这些外部引用者否则会把仍被引用的对象误回收。但全堆扫描违背了 G1 局部回收的初衷。解决办法是Remembered Set记忆集合RSet每个 region 维护一个 RSet记录有哪些来自其他 region 的指针指向了我。回收时只要扫描本 region 的 RSet 指向的少量 card就能精确找到外部引用而不必遍历整个堆。G1 的 RSet 并不记录对象级引用而是记录到card卡片粒度——card 是堆上一段固定大小默认 512 字节的内存块由 Card Table 管理。ptr card_shift即可把一个对象地址映射到它所属的 card。2. 引用变化如何通知 RSet写屏障对象的引用发生变化赋值如果不被感知RSet 就会失效。G1 通过写屏障Write Barrier拦截每一次引用字段的写入G1RemSet::write_ref_field / par_write_ref └─ g1RemSet.inline.hpp:69-72 HeapRegion* to _g1-heap_region_containing(obj); if (from ! to) { to-rem_set()-add_reference(p, tid); // 把p 指向 obj记入 obj 所在 region 的 RSet }要点被引用对象obj所在的 region 是被指向方toRSet 记在 to 上。from ! to说明这是一次跨 region的引用才需要进 RSetregion 内部引用不影响增量回收无需记录。tid是触发写入的线程 id用于并发与 FromCardCache 去重。3. 类层级HeapRegion → HeapRegionRemSet → OtherRegionsTableHeapRegion (heapRegion.hpp:211 _rem_set; 491 rem_set()) └─ HeapRegionRemSet (heapRegionRemSet.hpp:229, 内含 OtherRegionsTable _other_regions) └─ OtherRegionsTable (heapRegionRemSet.hpp:120, RSet 主体即下文PRT 容器) ├─ _sparse_table (SparsePRT稀疏) ├─ _fine_grain_regions (PerRegionTable** 开放哈希表细粒度 —— 源码真正的 PRT) └─ _coarse_map (BitMap粗粒度)术语澄清容易读混常见资料把OtherRegionsTable称为 “PRTPer Region Table”。但在 Oracle 这份源码里注释heapRegionRemSet.hpp:100-104明确写道_fine_grain_entries array is an open hash table of PerRegionTables (PRTs)——也就是说源码中真正的 PRT PerRegionTable类而OtherRegionsTable是PRT 的集合/管理器。本文用源码口径PRT PerRegionTable。HeapRegionRemSet本身是个薄壳所有实际记录工作都转交给OtherRegionsTableheapRegionRemSet.hpp:307-314。4. OtherRegionsTable 的三级存储OtherRegionsTable要回答的唯一问题是“哪些来自其他 region 的card 含有指向我owner region的指针”它用三级粒度来回答本质是在记录精度与内存占用之间做权衡引用越稀疏越省空间引用越密集越追求精确。4.1 Coarse粗粒度——_coarse_map结构一整张BitMapheapRegionRemSet.hpp:128位数 整个堆的 region 总数。存储的值只有1 bit 一个 region 索引。第i位为 1 表示regioni可能包含指向我的指针不记录是哪个 card。写入命中即跳过if (_coarse_map.at(from_hrm_ind)) return;heapRegionRemSet.cpp:457。置位动作发生在 fine 表满、淘汰 PRT 时delete_region_table()把被淘汰 PRT 对应的 region 在_coarse_map中置 1cpp:622-625称为粗化coarsen。性能✅ 查询 O(1)一次 bit 测试内存极小N 个 region 只花 N bit是所有方案里最省空间的兜底。❌ 代价最大GC 扫描时因为不知道具体 card必须扫描整个引用者 region 的所有对象/card扫描成本最高。用_n_coarse_entries统计粗化数量。4.2 Fine细粒度—— PerRegionTable源码真正的 PRT结构_fine_grain_regions是PerRegionTable**数组heapRegionRemSet.hpp:132即开放哈希表 冲突链。长度_max_fine_entriescpp:284-286 2^log2(G1RSetRegionEntries)默认基数 256再乘region_size_log_mb1。哈希ind from_hrm_ind _mod_max_fine_entries_mask冲突用_collision_list_next串成链。每个PerRegionTable存什么heapRegionRemSet.cpp:41_hr引用者 region 指针_bmBitMap长度 CardsPerRegion一个 region 内的 card 总数每一位对应引用者 region 内的一个 card存储的值把该引用者 region 内哪些 card 含指向 owner 的指针编码进 bit 位。add_reference_workcpp:89把from转成相对该 regionbottom()的 card 偏移hw_offset (card_shift - LogHeapWordSize)再add_card_work置位_bm对应 bit并行用par_at_put 原子自增_occupied。写入/查询prt-add_reference(from)置位contains_reference直接return _bm.at(card_ind)。性能✅ 最精确GC 只扫被置位的少数 card而非整个 region扫描成本最低。内存每个 PRT 固定带一张CardsPerRegion位的 BitMap本身不便宜但数量有硬上限默认 256 量级总体有界。上限与淘汰粗化fine 表满_n_fine_entries _max_fine_entries触发delete_region_table()cpp:578在采样窗口_fine_eviction_stride/_fine_eviction_sample_size里挑选occupied 最大的 PRT把它对应的 region 粗化置 coarse bit并立即init复用该 PRT 给新 regioncpp:509。即牺牲精度换内存有界。查找无锁、改桶链才持_mPRT 通过静态_free_list对象池复用cpp:206-203避免频繁 new/delete。遍历高效_first_all_fine_prts/_last_all_fine_prts双向链表批量操作所有 fine PRT 时不必扫整个哈希表。4.3 Sparse稀疏——_sparse_tableSparsePRT为什么存在当一个引用者 region 只贡献了极少 card时为它建一张整 BitMap 的 PRT 太浪费先用紧凑结构表示。结构SparsePRT内含两张RSHashTable_cur/_next双缓冲sparsePRT.hpp:216-217。每张RSHashTable_buckets桶数组容量为 2 的幂用capacity_mask()取模哈希_entriesSparsePRTEntry连续数组变长对象桶内用SparsePRTEntry::_next_index串成冲突链。SparsePRTEntry存什么sparsePRT.hpp:45_region_ind引用者 region 索引_cards[1]变长数组存该 region 内、含指向 owner 指针的 card 偏移相对 region bottom最多cards_num()个cards_num()MAX2(G1RSetSparseRegionEntries ~(4-1), 4)sparsePRT.hpp:64默认基数G1RSetSparseRegionEntriesBase 4所以每个 entry 默认最多存 4 个 card且数量恒为 4 的倍数配合UnrollFactor 4循环展开。存储的值(引用者 region 索引 → 至多 4 个 card 偏移)的紧凑映射。写入/溢出add_card在 entry 的_cards里找空位放入sparsePRT.cpp:93带 4 路展开若 4 个槽都满 → 返回overflow→ 上层在OtherRegionsTable::add_reference走建/复用 PRT 把 sparse 卡迁过去分支heapRegionRemSet.cpp:528-541即sparse → fine 升级promote。性能✅ 最省内存只有真正有引用的 region 才占 entry且每个 entry 仅存几个 int 级 card 偏移而非整张 BitMap。哈希表初始容量仅 16InitialCapacitysparsePRT.hpp:222利用率 50%occupied_entries*2 capacity自动expand()sparsePRT.cpp:483。双缓冲_cur/_nextsparsePRT.hpp:213-217 注释GC 扫描迭代只读_cur新增/删除走_next从而支持unsynchronized reads/iterations并发读不被并发改破坏cleanup 阶段再 reconcile。循环展开UnrollFactor4加速_cards的查找与拷贝。❌ 代价查找需哈希 冲突链遍历比 coarse 慢单 entry card 上限小超过即升级到 fine用空间换精度。5. 升级路径与总体调度引用少 ── Sparse (每引用者 region 至多 4 个 card 偏移极省内存) │ 某 region 存满 4 个 card 再插第 5 个 → overflow ▼ 引用增多 ── Fine / PerRegionTable (整张 card 位图精确扫描最省) │ fine 表达上限 (_max_fine_entries默认 ~256) ▼ 过多引用者 ── Coarse (1 bit/region牺牲扫描精度保住内存上限)sparse → fine单 entry 4 个 card 溢出触发 promote并把已存的 sparse 卡迁入新建 PRT。fine → coarsefine 表满淘汰 occupied 最大的 PRT 并粗化cpp:578。整体策略是能省则省、逐级退让动态平衡记录开销与GC 扫描开销。6. 跨三级的通用加速FromCardCache 去重FromCardCacheheapRegionRemSet.hpp:50每线程 × 每 owner region 缓存最近处理的 card 索引是横跨三层的优化。add_reference一进来先查它FromCardCache::contains_or_replace(tid, cur_hrm_ind, from_card)cpp:444——同一线程、同一 owner region、同一 card 刚处理过就直接返回避免重复入表。这在高频赋值的写密集场景下显著减少重复工作。7. 并发与内存序OtherRegionsTable头部注释heapRegionRemSet.hpp:106-118明确其并发策略查找 PRT 可以无锁find_region_table但修改桶链必须持_mMutex。允许并发读到正在被删除/复用的 PRT这是安全的因为PRT 只会在安全点真正释放平时是被复用cpp:113删除 PRT 时会先置coarse_map的 bit所以漏记不了复用 PRT 时即使误加 bit 也无害cpp:116-118。add_reference_work用is_in_reserved_raw防御并发复用cpp:109新建 PRT 用OrderAccess::release_store_ptr发布cpp:525保证内容可见后才对外可用。8. 小结粒度存储结构存的值查询内存扫描精度Coarse1 bit/region_coarse_mapregion 索引可能存在O(1)极小必须扫整个引用者 regionFine / PRTPerRegionTable card 位图引用者 region 内具体 card 偏移O(1) 位测试中数量有上限只扫置位 card最精确SparseSparsePRT(region→≤4 card) 紧凑哈希region 索引 至多 4 个 card 偏移哈希链极小只扫记录的 cardG1 用这套能省则省、逐级退让的三级存储在记录成本维护 RSet 的写屏障开销、内存与GC 扫描成本回收时遍历 RSet 的开销之间取得了动态平衡这正是 RSet 能支撑 G1 增量、可预测停顿回收的关键设计。附关键源码位置速查内容文件:行HeapRegion::rem_set()heapRegion.hpp:491HeapRegionRemSet类heapRegionRemSet.hpp:229OtherRegionsTable类 / 三级字段heapRegionRemSet.hpp:120, 128/132/149add_reference主流程heapRegionRemSet.cpp:425delete_region_table粗化heapRegionRemSet.cpp:578PerRegionTable类 /add_reference_workheapRegionRemSet.cpp:41 / 89SparsePRT/SparsePRTEntrysparsePRT.hpp:210 / 45写屏障入口to-rem_set()-add_referenceg1RemSet.inline.hpp:72FromCardCache去重heapRegionRemSet.cpp:444

相关新闻

2026/8/14 22:38:56

RAG+Agent主程序架构设计:从核心原理到生产级实现

1. 从概念到现实:为什么RAGAgent是当前AI应用的核心范式如果你最近在关注AI应用开发,尤其是想构建一个能真正“理解”并“使用”你私有数据的智能助手,那么“RAGAgent”这个组合你一定绕不开。它不再是实验室里的概念,而是正在成为…

2026/8/14 22:38:55

XXL-Job双实例重复执行问题排查:从数据库主键失效到时钟同步陷阱

最近在排查一个线上任务重复执行的问题,客户反馈明明配置了单机串行执行,但日志里却出现了同一任务在同一时间被两个不同的执行器实例执行了两次。这直接导致了数据重复处理,下游系统报错。问题指向了分布式任务调度框架 XXL-Job,…

2026/8/14 22:38:55

AI前沿日报_2026-08-13

🤖 AI 前沿日报:2026年8月13日三大模型同日发布:Qwen 3.8开源2.4万亿参数MoE、Grok 4.6对标GPT-5.6、DeepSeek V4 Pro上线API;AI代理自主发现开源项目安全漏洞并获人类维护者合并;OpenAI伦理负责人入职不到一年辞职&am…

2026/8/14 23:23:59

退股不是简单一拍两散

股东想要退股,不能仅凭口头约定。有限公司股权对外转让需其他股东优先购买,内部转让也要书面协议。未完成工商变更、未理清债权债务,即便拿到钱款,依然可能承担公司债务风险,规范流程才能彻底脱身。

2026/8/14 23:23:59

风电场高压开关柜无线测温应用:及时预警触头超温避免重大故障

摘 要:作为保证系统安全运行的重要设备的高压开关柜装置,其内部导电连接处过热会导致电气设备的损坏,更严重的是可能还会发生火灾等严重事故。因此,电力系统中电器设备安全运行的一个重要课题就是对高压开关柜实施在线监测。本文主要研究的是针对高压开关柜接点的无…

2026/8/14 23:23:59

2027北京AI数字健康与智慧医疗展官方:3.5万平双馆旗舰展

为承载持续增长的参展企业数量与行业交流需求,2027北京AI数字健康与智慧医疗展(赛逸展)全面升级展览规模,落地北京亦创国际会展中心A、C双馆联动布局,整体展出总面积达到3.5万平方米,正式成为国内AI数字健康…

2026/8/14 23:23:58

Android-EDLA STS测试全流程介绍

文章目录Android-EDLA STS测试全流程介绍1.软件版本2.STS套件下载(需要VPN网络)3.STS测试命令4.配置STS测试环境4.1配置frida-XXX.xz文件4.2配置ghidra_XXX.zip文件Android-EDLA STS测试全流程介绍 1.软件版本 测试STS需要debug版本的软件。 2.STS套件…

2026/8/14 23:18:58

超市实体经营感悟:换位思考服务用户,方能实现长期成长

我是广东万民商业有限公司超市从业者钟华源,深耕实体商超行业多年,本次通过观摩学习优质零售门店的运营模式,让我对实体零售的服务本质与经营逻辑,有了全新且深刻的认知。在行业发展过程中,很多门店经营往往聚焦于商品…

2026/8/14 4:27:24

如何快速生成中国车牌图片:Python开源工具完整指南

如何快速生成中国车牌图片:Python开源工具完整指南 【免费下载链接】chinese_license_plate_generator 中国车牌生成器 项目地址: https://gitcode.com/gh_mirrors/ch/chinese_license_plate_generator 中国车牌生成器是一个基于Python的开源项目&#xff0c…

2026/8/14 4:27:24

当 LLM 遇见大文档:主流开源项目如何处理上下文超限

从 Agentic Loop 到 Repo Map,七种策略与六类陷阱引言:128K vs 10MB 的硬冲突 2026 年的 LLM 上下文窗口已达到 128K ~ 1M token(≈ 0.5MB ~ 4MB 文本),但 LLM 想要处理的真实数据规模远远超过这个量级:真实…

2026/8/14 0:00:09

Flutter与OpenHarmony实现剧本杀组队表单开发实战

1. 项目概述在移动应用开发领域,跨平台框架Flutter因其高效的开发体验和出色的性能表现,已经成为众多开发者的首选。而OpenHarmony作为新兴的操作系统平台,其开放性和灵活性为开发者提供了全新的可能性。本文将聚焦于一个实际应用场景——剧本…

2026/8/14 0:00:09

VSCode高效Git管理:从入门到实战技巧

1. 为什么选择VSCode进行Git代码管理作为微软推出的轻量级代码编辑器,Visual Studio Code(简称VSCode)已经成为全球开发者使用率最高的编辑器之一。根据2023年Stack Overflow开发者调查,VSCode的市场占有率高达74.48%。它内置的Gi…

2026/8/14 4:27:24

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/14 4:27:24

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/14 4:27:24

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…