{ "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": [] } ] }