Files
examination/topics/gc-special/gc-deep-dive/short_answer.json
T

278 lines
29 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"topic": "gc-deep-dive",
"type": "short_answer",
"schema_version": "1.0.0",
"generated": "2026-09-03T00:00:00Z",
"questions": [
{
"id": "sa-001",
"type": "short_answer",
"difficulty": 2,
"tags": [
"GC",
"引用计数",
"可达性分析",
"基础概念"
],
"question": "简述垃圾回收(Garbage Collection)是什么,其中的对象会被什么方式判定为垃圾?请对比两种主要判定方式——引用计数 与 可达性分析——的差异、优缺点各列出至少 2 条。",
"answer": "垃圾回收是一种自动内存管理机制,用于自动识别并回收那些不再被程序引用的对象所占用的内存,避免手动 free 造成的悬垂指针和内存泄漏。判定垃圾的两种主流方式如下。\n\n一、引用计数(Reference Counting)\n- 原理:为每个对象维护一个引用计数器,每当有一个新引用指向它时计数 +1,引用被移除时计数 -1;当计数归 0 时对象被立即回收。\n- 优点:(1) 回收及时,对象成为垃圾的当下即可被回收,无停顿感;(2) 实现简单,增量式完成,不需要全局扫描,适合实时性要求高的场景。\n- 缺点:(1) 无法解决循环引用——A 引用 B、B 引用 A 时两者计数永不为 0,导致垃圾无法回收造成泄漏;(2) 每次赋值操作都要同步修改计数器,带来额外性能开销;(3) 计数更新不是原子的,多线程下需要同步。\n\n二、可达性分析(Trace / Reachability Analysis,如从 GC Roots 遍历)\n1. 原理:从一组被称为 GC Roots 的根对象出发,沿引用链向下遍历,凡是能到达的对象为存活对象,其余不可达的对象即为垃圾,交给收集器回收。\n2. 优点:(1) 能正确解决循环引用,循环引用的对象只要不被根可达,整体仍会被回收;(2) 不需要对每次赋值操作计数,开销集中在 GC 阶段。\n3. 缺点:(1) 需要 Stop-The-World 扫描,停顿相对较长(可通过分代、并发优化缓解);(2) 实现复杂度较高。\n\n差异总结:引用计数看重对象被引用的次数,即时、增量;可达性分析看重对象是否从根可达,周期性、批量。现代主流 JVM(HotSpot)采用可达性分析。",
"keywords": [
"垃圾回收",
"引用计数",
"可达性分析",
"循环引用",
"GC Roots",
"引用计数优点",
"可达性分析优点",
"内存回收"
],
"scoring_rubric": "解释垃圾回收概念 1 分;引用计数原理与至少 2 条优缺点 3 分;可达性分析原理与至少 2 条优缺点 3 分;明确指出循环引用是二者关键差异 1~2 分;比较与总结 1 分。总分 10 分。",
"explanation": "本题目的是打基础,考察对 GC 判定方式本质的把握。引用计数与可达性分析的最大分野在于对循环引用的处理,回答时两者优缺点都能举出 2 条以上、并触及循环引用,即可拿高分。",
"source": null,
"related": []
},
{
"id": "sa-002",
"type": "short_answer",
"difficulty": 3,
"tags": [
"分代收集",
"年轻代",
"老年代",
"晋升"
],
"question": "为什么要对堆进行分代(Generational)收集?请简述年轻代/老年代的结构,以及对象从年轻代晋升(Promotion)到老年代的过程,说明分代收集的理论依据。",
"answer": "1. 分代的理论依据是弱分代假设(Weak Generational Hypothesis):绝大多数对象存活时间极短,很快成为垃圾;只有少部分对象会长期存活。针对不同存活特质的对象采用不同回收策略,能在保持回收效果的同时大幅降低扫描与复制成本。\n\n2. 年轻代(Young Generation)\n1. 通常包含 Eden 区与两个 Survivor 区(S0、S1,等大小)。新对象分配在 Eden。\n2. 特点:回收时存活率低、垃圾占比高,适合采用复制算法——把存活对象复制到 Survivor,一次性清空 Eden 与被清理的 Survivor,无碎片。\n3. Young GC / Minor GC:仅扫描年轻代,速度快。\n\n3. 老年代(Old Generation)\n- 存放存活时间长、晋升上来的对象。存活率高,不适合反复复制,常用标记清理或标记压缩。\n- 老年代空间不足或分配担保失败时触发 Major / Full GC。\n\n4. 晋升过程(对象从年轻人进入老年的完整链路)\n1. 存活对象在 Minor GC 中被复制到 Survivor,每经过一次 Minor GC 且存活,其年龄(age)就 +1。\n2. 当年龄到达阈值(HotSpot 默认 15,可用 -XX:MaxTenuringThreshold 调整),在后续 GC 时晋升入老年代。\n3. 大对象(超过 -XX:PretenureSizeThreshold)直接在老年代分配,避免反复复制。\n4. 若 Survivor 中存活对象过多导致空间不足,超过容量的部分会提前动态晋升到老年代。\n\n5. 总结:年轻代 GC 频繁但对空间、耗时短;老年代 GC 频率低但耗时长,兼顾吞吐与停顿的平衡。",
"keywords": [
"分代",
"弱分代假设",
"Eden",
"Survivor",
"晋升",
"年龄 15",
"Promotion",
"复制算法"
],
"scoring_rubric": "答出理论依据(弱分代假设/大部分对象短命)2 分;年轻代结构 Eden+Survivor 及回收方式 3 分;对象晋升过程(年龄+1、到 15、Survivor 满提前晋升、大对象直接入老年代)逐项给分 4 分;总结 1 分。总分 10 分。",
"explanation": "分代是 HotSpot 的核心思路。应抓住为什么分代(短命对象多)与怎么分(年轻代复制、老年代标记压缩)以及晋升的完整链路。",
"source": null,
"related": []
},
{
"id": "sa-003",
"type": "short_answer",
"difficulty": 4,
"tags": [
"CMS",
"G1",
"并发标记",
"停顿"
],
"question": "对比 CMS(Concurrent Mark Sweep)与 G1(Garbage First):为什么 G1 取代了 CMS 成为默认收集器?请具体列出 CMS 至少在 3 个方面的缺陷,并说明 G1 是如何针对性地进行改进的。",
"answer": "CMS 以最小化停顿为目标,大部分标记与清扫阶段与用户线程并发执行;但并发阶段与用户线程争抢 CPU、且会产生大量内存碎片,最终被 G1 取代。G1 从 JDK 9 起成为默认收集器。\n\n一、CMS 的主要缺陷\n1. 内存碎片:采用标记-清除,产生大量内存碎片,老年代空间不连续,导致需要频繁以 Full GC 作为补偿(Full GC 需要 STW,停顿极长)。\n2. 浮动垃圾(Floating Garbage):并发阶段新产生的垃圾无法在本轮回收,只能推迟到下次 GC,可能引发提前的 Full GC。\n3. CPU 争抢:并发标记/清理阶段与用户线程争抢 CPU,在 CPU 资源紧张的机器上吞吐和性能下降明显。\n4. 复杂度高、临界失败代价大:过程分为初始标记、并发标记、重新标记、并发清理、重置五阶段,出现 concurrent mode failure 时回退为串行的 Full GC。\n5. 仅针对老年代:年轻代需搭配 ParNew 等收集器,二者需协同。\n\n 二、G1 的针对性改进\n1. Region 化:不再物理划分年轻代/老年代,把堆划分为大量相同大小的 Region,每次只回收价值最大的若干 Region(故称 Garbage First),以可预测停顿模型替代 CMS 的模糊停顿。\n2. 复制+压缩:对目标 Region 使用复制/压缩而非标记清除,回收后空间连续,大幅降低内存碎片,减少 Full GC 概率。\n3. RSet(Remembered Set):按 Region 记录跨区引用,只回收部分 Region 时不必扫描整个堆,减少 GC 工作量。\n4. 并发标记 + 可预测停顿:同样采用并发标记(三色标记)实现低停顿,并通过可预测停顿模型控制停顿时间。\n\n结论:G1 以 Region 化 + 压缩式回收 + RSet 定位 + 可预测停顿设计,从根上解决了 CMS 的碎片、浮动垃圾与不清楚的停顿问题。",
"keywords": [
"CMS缺陷",
"G1",
"Region",
"内存碎片",
"Full GC",
"浮动垃圾",
"可预测停顿",
"并发标记"
],
"scoring_rubric": "答出 CMS 至少 3 个确定性缺陷(碎片、CPU 争抢、浮动垃圾、临界失败)各 1 分共 3~5 分;答出 G1 的对应改进(Region、复制/压紧、RSet、可预测停顿)2~3 个 4 分;能说明取消替代关系 1 分。总分 10 分。",
"explanation": "本题考查对 CMS 方案与 G1 设计逻辑的理解。核心是 CMS 的『标记-清除』天然带来碎片与浮动垃圾,G1 通过 Region + 压缩 + RSet 彻底革新了处理体系。",
"source": null,
"related": []
},
{
"id": "sa-004",
"type": "short_answer",
"difficulty": 3,
"tags": [
"GC代价",
"StopTheWorld",
"性能"
],
"question": "请列举垃圾回收带来的代价,尽量详细(最好 7 条左右),每条用一句或一两句说明其影响。",
"answer": "垃圾回收虽是现代语言的自动内存管理手段,但也会带来一系列不可忽视的代价:\n\n1. Stop-The-World(STW)停顿:GC 过程中需要暂停应用线程,导致服务卡顿、延迟升高——这是最核心的代价。\n2. CPU 开销/吞吐量损失:GC 本身要消耗 CPU(标记、复制、压缩、并发线程等),与业务争抢 CPU,降低整体吞吐量。\n3. 内存开销/占用率:GC 需要额外的堆内存管理(如复制算法要预留等大 Survivor 空间、Region 划分、元数据如对象头、RSet、标记位图),使有效可用内存减少。\n4. 内存碎片化:尤其标记-清除导致空闲内存不连续,无法分配大对象,后续只能 Full GC 或反复整理。\n5. 浮动垃圾堆积:并发/增量式 GC 无法收集标记后新产生的垃圾,导致内存利用率下降、回收延迟。\n6. 吞吐与延迟的二择:专注于低延迟的收集器(CMS/G1/ZGC)往往牺牲吞吐与额外 CPU,懒人反而牺牲延迟,二者难以兼得。\n7. 延迟不稳定性与抖动(Lag Spike):GC 发生时机不可预测,导致业务延迟随机抖动,影响响应稳定性。\n\n(附加)8. 调优与运维成本:GC 日志分析、参数调优、故障排查均带来额外复杂度。",
"keywords": [
"Stop-The-World",
"CPU开销",
"内存占用",
"碎片化",
"浮动垃圾",
"吞吐量",
"延迟抖动",
"调优成本"
],
"scoring_rubric": "每答对 1 个清晰合理的代价记 1 分,能联系 STW/碎片/浮动/资源与资源竞争等核心概念;列出 7 条即满分 8 分以上,弹性给分。逻辑清晰、触及本质者可给加分。",
"explanation": "GC 的七大代价是底层思考常见考点。核心围绕 STW、CPU/内存资源成本、碎片、浮动垃圾、吞吐与延迟的权衡展开,答案覆盖关键词即可。",
"source": null,
"related": []
},
{
"id": "sa-005",
"type": "short_answer",
"difficulty": 4,
"tags": [
"三色标记",
"并发标记",
"漏标",
"SATB"
],
"question": "详细说明三色标记法(Tricolor Marking)的实现原理(白/灰/黑三种颜色的含义与转移过程),并分析并发标记过程中可能出现的『漏标』与『多标』问题,说明各自的来源及解决方案(如 SATB、写屏障、增量更新)。",
"answer": "三色标记法是一种用于增量/并发可达性分析的着色约定,把每个对象标记为三种颜色:\n\n一、颜色含义\n1. 白色(White):尚未被访问到的对象,初始所有对象都是白色;标记结束后仍为白色的即为不可达垃圾。\n2. 灰色(Gray):已被扫描访问到(已标记),但其引用的子对象还没有全部扫描完,灰色对象是待继续遍历的工作队列。\n3. 黑色(Black):该对象及其所有子引用都已被扫描完毕,不会再被遍历。\n\n二、标记过程\n1. 将 GC Roots 标记为灰色,放入工作栈。\n2. 循环弹出灰色对象,扫描其引用的所有子对象:若子对象为白色则将其变灰;该父对象全部子对象扫描完后变黑。\n3. 重复直到栈为空,此时剩余白色对象即为不可达垃圾,被回收。\n\n三、并发标记的问题\n由于标记阶段与用户线程并发,用户线程会修改引用,带来两个问题:\n\n1. 漏标(Missing Mark):应存活的对象被误判为白垃圾被错误回收,造成引用悬空(最严重)。经典触发条件是:黑色对象 A 新增了对白色对象 B 的引用,同时原本指向 B 的灰色对象 C 又删除了对 B 的引用。因为黑色对象不会再被扫描,B 被永久漏掉。\n- 解决:使用写屏障,保证黑色对象不会新增指向白色对象的引用。两种策略:\n- 写后记录(增量更新 Incremental Update):当黑色对象 A 的引用被修改时,把新指向的白对象 B 重新置灰,下一轮再扫描,CMS 采用。\n- 写前记录(SATB 起始快照 Snapshot-at-the-Beginning):在删除旧引用时,把旧对象标记为灰色(无论后续是否删除),G1 采用。\n\n2. 多标(Floating Garbage / 浮动垃圾):本应死亡的白色对象,在标记途中仍被活跃灰对象引用,或并发期间新产生的垃圾,导致本应回收的对象被标记成存活而延时回收。多标只是标记过多,不影响正确性(不会误回收存活对象),是并发收集可接受的少量延迟回收。\n\n四、保障结论\n通过写屏障正确保证黑色对象不可新增指向白色对象的引用,即可确保不漏标;SATB 与增量更新是两种解决漏标的写屏障策略,G1 默认 SATB,CMS 默认增量更新。",
"keywords": [
"三色标记",
"白色灰色黑色",
"漏标",
"多标",
"写屏障",
"SATB",
"增量更新",
"浮动垃圾"
],
"scoring_rubric": "三色含义与标记过程正确 3 分;漏标原理(需答出灰色转黑后黑色新增引用白色的必要条件)3.5 分;解决办法(写屏障、SATB 与增量更新的对照)+多标与浮动垃圾 3.5 分。总分 10 分。",
"explanation": "本题考查并发标记的线头。关键是以「A 黑引用 B 白 + B 的灰色引用被删除」讲清楚漏标的必要条件,并对应记忆 G1 用 SATB、CMS 用增量更新作对照。",
"source": null,
"related": []
},
{
"id": "sa-006",
"type": "short_answer",
"difficulty": 3,
"tags": [
"标记清除",
"标记压缩",
"复制算法",
"算法对比"
],
"question": "对比三种基本垃圾收集算法——标记-清除(Mark-Sweep)、标记-压缩(Mark-Compact)、复制(Copying)算法——各自原理、优缺点各列出 2 条,以及适合的场景。",
"answer": "一、标记-清除(Mark-Sweep)\n- 原理:先标记所有存活对象,再统一清除所有未被标记的对象,释放其空间。\n- 优点:(1) 实现最简单直接;(2) 不移动对象,无拷贝搬移开销。\n- 缺点:(1) 产生大量内存碎片,空闲空间不连续,难以分配大对象;(2) 需要扫描整个堆的存活与死亡对象,工作量随堆增大而上升(效率依赖堆大小)。\n- 适用:适合存活对象多、堆小、对碎片要求不高的场景(常为老年代的粗糙版本)。\n\n2. 标记-压缩(Mark-Compact)\n- 原理:先标记存活对象,再存活对象向堆的一端搬移压缩,使它们连续排列,然后清理边界之外的区域,并更新所有引用。\n- 优点:(1) 消除内存碎片,空间连续可分配大对象;(2) 不浪费额外空间。\n- 缺点:(1) 移动对象需更新所有引用,搬移成本高,耗时可能较长;(2) 实现复杂度高,STW 可能较长。\n- 适用:老年代(存活率高,复制算法不划算)。\n\n3. 复制算法(Copying / Mark-Copy)\n- 原理:把可用内存等分为两块(或 Eden + Survivor),每次只用其中一块;回收时把存活对象复制到另一块连续空间,然后把当前块整体清空。\n- 优点:(1) 复制后空间连续无碎片,分配高效;(2) 只遍历存活对象,存活率低时效率极高,复制即回收。\n- 缺点:(1) 需要预留等大的额外空间(即内存利用率减半);(2) 存活率高时复制量大、效率下降。\n- 适用:年轻代(存活率低、对象短命)。\n\n总结:复制算法适合年轻代,标记-存在与标记-压缩适合老年代;碎片/存活率是选择算法的核心权衡。",
"keywords": [
"标记清除",
"标记压缩",
"复制算法",
"内存碎片",
"存活率",
"复制",
"算法效率",
"适用范围"
],
"scoring_rubric": "每种算法:原理 1 分 + 优缺点各 2 分 + 适用场景 1 分,三种合计近 12 分按 10 分制折算;能较横向比较(复制→年轻代、清除→碎片、压缩→老年代)与碎片/存活率权衡逻辑更柔。",
"explanation": "三大基本算法的本质区别在于:清除浪费空间产生碎片、压缩重排消灭碎片、复制以空间换时间(只接触存活对象)。难度偏基础,重点考察对比思维。",
"source": null,
"related": []
},
{
"id": "sa-007",
"type": "short_answer",
"difficulty": 3,
"tags": [
"内存碎片",
"碎片化",
"分配",
"解决方式"
],
"question": "什么是内存碎片(Memory Fragmentation),它是如何产生的?会造成什么危害?请列出至少 3 种解决或缓解碎片问题的方案。",
"answer": "1. 定义:内存碎片指堆上的空闲内存被分割成大量不连续、零散的小块,导致虽然总的空闲内存足够,却找不到一块连续的足够空间来分配对象(尤其大对象)。分两类产生原因:内容累计分隔块的两类,碎片含内碎片与外部碎片。\n\n2. 产生原因:\n1. 主要源于使用标记-清除等不定长分配方式:每次只释放已死亡所占的空洞,长期下来空间被切成大量小块。\n2. 对象不断分配与释放、大小不一,缺乏整理;或分配器策略设计不当。\n\n3. 造成的危害:\n1. 无法每次分配大对象,GC 提前触发或分配失败,进而退化升级为代价高昂的 Full GC / 压缩 GC。\n2. 降低堆利用率,分配需寻找合适空洞,降低整体分配速度。\n3. 对实时/低延迟系统造成难以预料的停顿与延迟抖动。\n\n4. 解决/缓解方案:\n1. 标记-压缩(Mark-Compact):把存活对象搬到一起,消除碎片。\n2. 复制算法(Copying):使用 Eden/Survivor 对称空间,回收时把存活对象复制到全新连续区,天然无碎片。\n3. Region/分区式大堆:如 G1、ZGC 把堆切成 Region,回收后把 Region 整理成连续的空白空间复用。\n4. 分配器优化:按对象大小分类的桶/分块管理、对象复用、内存池,减少碎片产生。\n5. 结合滚动/并发压缩的收集器(CMS 结合 CD、G1 换代)逐步整理。",
"keywords": [
"内存碎片",
"碎片产生",
"标记压缩",
"复制算法",
"Region",
"Full GC",
"连续空间",
"内存池"
],
"scoring_rubric": "概念 2 分;产生原因(清除造成不连续、分配释放)2 分;危害 ≈2 分;列出 3 种及以上方案各 1~2 分(压缩、复制、G1 Region)合计 8~10 分为满分。",
"explanation": "此题将『碎片』这一具体现象与算法利弊衔接:清除形成碎片、压缩与复制解决碎片是核心链路,解决方案可延伸至 G1/ZGC 的 Region 化设计。",
"source": null,
"related": []
},
{
"id": "sa-008",
"type": "short_answer",
"difficulty": 4,
"tags": [
"ZGC",
"染色指针",
"读屏障",
"亚毫秒"
],
"question": "为什么 ZGC(Z Garbage Collector)能实现亚毫秒级别的 GC 停顿?请从它的收集目标、关键机制(染色指针、读屏障、Region/内存布局)几个方面系统说明。",
"answer": "ZGC 是一款基于 Region 的并发垃圾收集器,设计目标是把 Stop-The-World 时间控制在 10ms 以内,很多场景可达亚毫秒。其停顿极短来自与 G1 截然不同的成套设计:\n\n1. 几乎全并发:ZGC 几乎把所有阶段(标记、转移、重映射)都与用户线程并发执行,仅极短的根本扫描、初次标记临界等阶段需要短暂停顿,且停顿主要取决于 GC Root 数量,几乎与堆大小无关。\n\n2. 染色指针(Colored Pointers / 彩色指针):ZGC 不把标记信息放进对象头,而是直接利用 64 位指针的高位段(含 Finalizable、Remapped、M0/M1 等标识位)来承载对象的 GC 状态。判断对象状态时无需访问对象、无需加锁,随指针读取原子完成,标记开销极低;这也是它支持超大堆(TB 级)的关键。\n\n3. 读屏障(Read Barrier / Load Barrier):当用户线程读取引用时,若目标对象的转移/重映射状态未满足,读屏障会在指令微操作内自动把该对象并发迁移(Move)或重新映射(Remap)到新位置再返回。这样对象迁移过程无需停止应用线程,读(解引用)这一件事本身完成了迁移,正确性与低停顿兼得。\n\n4. 并发压缩与重映射(Concurrent Relocation & Remapping):回收时把存活对象复制到新的空闲 Region,借助转发指针对旧地址映射到新地址;应用在读取时由读屏障触发重映射,转移过程全部并发完成,无 STW 式全局更新引用。\n\n5. 多分桶 Region 内存 = 分堆为多种规格的 Region 以降低内借助;回收后 Region 被整理复用。\n\n综合:ZGC 把标记信息注入指针、用读屏障把迁移与重映射都并发化、同时只依赖 GC Roots 数量的停顿,从而把 STW 压到亚毫秒。代价是额外的读写屏障与吞吐开销。",
"keywords": [
"ZGC",
"染色指针",
"读屏障",
"亚毫秒停顿",
"并发回收",
"重映射",
"Region",
"GC Roots"
],
"scoring_rubric": "答出并发化几乎所有阶段 2 分;染色指针(把标记写入指针高位段)2~3 分;读屏障并发迁移/重映射 2~3 分;Region+并发压缩与停顿随堆交互的结论 2~3 分。综合 9~10 分。",
"explanation": "ZGC 的低停顿是多个设计协奏的结果:染色指针(把 GC 状态写进指针)+ 读屏障(读取时完成迁移)+ 并发重映射。抓住『几乎全并发、停顿只依赖根数量』即可把握本质,注意与 G1 的定位区分。",
"source": null,
"related": []
},
{
"id": "sa-009",
"type": "short_answer",
"difficulty": 4,
"tags": [
"G1",
"Region",
"可预测停顿",
"RSet",
"SATB"
],
"question": "简述 G1(Garbage-First)的 Region 结构及核心概念,并重点解释 G1 是如何实现『可预测的暂停』的。(可涉及 Region、Collection Set、Remembered Set、按期收益选择回收区域等)",
"answer": "1. Region:\nG1 不再物理划分连续年轻代/老年代,而是把整个堆划分为大量相同大小的 Region(通常 1M~32M,为 2 的幂)。每个 Region 可为 Eden、Survivor、Old 或 Humongous(大对象)之一:\n- 对象分配在 Region 内,回收以 Region 为独立单元;\n- 超过一定规模的大对象(Humongous)占用连续多个 Region;\n- 年轻代/老年代变为 Region 的逻辑集合,比例可动态调整,回收集合更灵活。\n\n2. Remember Set(RSet)与 Card Table:\n- 每个 Region 维护一个 RSet,记录『哪些 Region 的对象引用了本区对象』(跨区引用反向记录)。\n- 回收某个 Region 时只需扫描其 RSet 记录的外部引用 + 本区根,而不必扫描全部堆,大幅缩小 GC 扫描范围。\n- 跨区引用的写入由写屏障记录进 Card Table/RSet。\n\n3. 可预测停顿:\n1. 用户可设定最大停顿目标(-XX:MaxGCPauseMillis,默认 200ms)。\n2. G1 通过全局并发标记统计每个 Region 的『垃圾占比』(存活率),回收价值按垃圾比例从高到低排序——Garbage First 语义。\n3. 每次收集由 Young GC / Mixed GC 组成:Young GC 清 Eden、少量回收;Mixed GC 额外回收垃圾价值高的 Old Region。\n4. 收集时按停顿预算(时间段)决定本次回收多少个 Region,选择 CSet(Collection Collection Set)时优先挑选回收性价比高的 Region,从而把停顿时间稳定控制在预算内,实现可预测的停顿。\n\n4. 并发标记配合 SATB 写屏障保证不漏标,RSet 提供快速引用定位。\n\n因此 G1 能以『回收价值高优先』为原则、配合 RSet 降低扫描成本、并按预算选择回收 Region,把停顿时间稳定控制在目标范围内,实现停顿可预测。",
"keywords": [
"G1",
"Region",
"RSet",
"可预测停顿",
"Remember Set",
"CSet",
"MaxGCPauseMillis",
"回收收益"
],
"scoring_rubric": "Region 划分与逻辑标签 3 分;讲清 RSet 作用(记录跨区引用,免全堆扫描)2 分;核心『按回收收益排序选 Region + 时间预算』3 分;能收敛到可预测停顿与 CSet/MaxGCPauseMillis 得 2 分。总分 10 分。",
"explanation": "G1 之所谓 Garbage-First 来源其整理化细分 Region 并量化每个 Region 垃圾占比,按收益+停顿预算动态选 CSet。理解 RSet(免全堆扫描)与按收益选择三环节,才能真正把握『可预测停顿』。",
"source": null,
"related": []
},
{
"id": "sa-010",
"type": "short_answer",
"difficulty": 3,
"tags": [
"GC Roots",
"可达性",
"循环引用",
"泄漏"
],
"question": "请列举 GC Roots(根对象)都有哪些?并解释为什么基于可达性分析的垃圾回收能正确判定对象的存活性,从而使得『循环引用』的两个对象也能被正确回收、不会造成内存泄漏(与引用计数的对比较)。",
"answer": "一、GC Roots 是可达性遍历的起始存活对象集合,常见的 GC Roots 包括:\n1. 虚拟机栈(栈帧中的本地变量表):线程栈帧中局部变量引用的对象。\n2. 本地方法栈中 JNI 引用的对象:Native 方法引用的全局、局部对象。\n3. 方法区中的静态属性/常量:static 字段引用的对象及其类型。\n4. 运行时常量池中的常量引用:例如字符串常量池中的字符串对象。\n5. 当前被 synchronized 加锁(monitor)的对象。\n6. 系统类加载器 C 一些长期常驻的类结构、被 JIT 编译占用的 etc。\n\n二、为什么循环引用也能被正确回收(对比引用计数)\n1. 引用计数的缺陷:它基于『是否还被其他对象引用』计数,当两个互相引用的对象(A↔B)再无外部引用时,计数器仍 ≥1、永不归零,导致它们永不被回收,造成内存泄漏。\n2. 可达性分析的判据:它不以『对象数量』为判据,而是判『能否从 GC Roots 根出发被访问到』——垃圾的定义是『从根不可达』。\n3. 因此,只要 A、B 组成循环引用且整体不被任何 GC Roots 直接或间接可达,系统从 Roots 出发就走不到这组对象,它们便作为『未标记(白色)』的孤岛整体被回收,回收后不形成泄漏。\n4. 结论:只要循环组是孤儿(岛孤岛),它就是可回收整体,可达性分析能安全地全部回收,从而彻底规避了引用计数的循环引用泄漏问题。只有真正连在来自根的引用链上的对象才被保留。",
"keywords": [
"GC Roots",
"本地变量",
"静态变量",
"常量池",
"synchronized",
"可达性",
"循环引用",
"泄漏"
],
"scoring_rubric": "GC Roots 能列全 4 种以上(栈变量、静态变量、常量池、monitor)、说明根可达判据与引用计数的差异、并准确解释循环孤儿岛会被整体回收,共给 8~10 分(逐条给分)。",
"explanation": "把握『GC 判定垃圾的唯一正确标准是从根可达』,从而理解计数法的循环引用死穴如何被根遍历机制自然化解——孤儿循环组作为整体回收即不泄漏。",
"source": null,
"related": []
}
]
}