【JVM虚拟机】垃圾回收GC:垃圾判定算法:引用计数法、可达性分析算法(附《思维导图》+《面试高频考点清单》)
文章目录JVM垃圾回收垃圾判定算法 系统性知识体系一、垃圾判定算法概述二、引用计数法Reference Counting2.1 基本原理2.2 实现细节2.3 核心优点2.4 致命缺点循环引用问题2.5 其他缺点2.6 优化方案2.7 实际应用三、可达性分析算法Reachability Analysis3.1 基本原理3.2 GC Roots集合核心重点3.3 实现细节3.4 核心优点3.5 主要缺点3.6 优化方案3.7 实际应用四、两种算法的全面对比五、扩展知识引用类型与垃圾判定的关系5.1 强引用Strong Reference5.2 软引用SoftReference5.3 弱引用WeakReference5.4 虚引用PhantomReference六、常见面试考点JVM垃圾判定算法 面试问答卡片可直接背诵一、基础核心题必背Q1什么是垃圾判定它在JVM垃圾回收中的作用是什么Q2简述引用计数法的基本原理和优缺点。Q3引用计数法的致命缺陷是什么请举例说明。Q4简述可达性分析算法的基本原理。Q5什么是GC Roots哪些对象可以作为GC Roots高频必背二、进阶对比题Q6引用计数法和可达性分析算法的核心区别是什么Q7为什么主流JVM如HotSpot选择使用可达性分析算法而不是引用计数法三、扩展优化题Q8可达性分析算法为什么需要Stop-The-WorldSTW停顿Q9HotSpot虚拟机为了优化可达性分析的效率做了哪些关键优化Q10Java中的四种引用类型分别是什么它们的区别和应用场景是什么高频必背背诵小贴士JVM垃圾判定算法 一页纸速记版考前必看一、基础核心优先级★★★★★二、进阶对比优先级★★★★☆三、扩展优化优先级★★★☆☆背诵优先级JVM垃圾回收垃圾判定算法 系统性知识体系一、垃圾判定算法概述垃圾判定是JVM垃圾回收GC的第一步核心任务是准确识别堆内存中哪些对象已经死亡不再被任何途径使用为后续的垃圾回收器提供回收目标。垃圾判定算法的设计目标准确性不能误判存活对象为垃圾导致程序崩溃高效性不能占用过多CPU时间和内存资源实时性尽量减少对应用程序执行的影响JVM主流实现中主要采用两种垃圾判定算法引用计数法和可达性分析算法。二、引用计数法Reference Counting2.1 基本原理为每个对象添加一个引用计数器用于记录该对象被引用的次数当有一个新的引用指向该对象时计数器值**1**当一个引用失效时如引用变量被赋值为null、超出作用域计数器值**-1**当计数器值变为0时判定该对象为垃圾可以被回收2.2 实现细节计数器通常存储在对象头Object Header中引用的赋值和失效操作需要原子性保证避免多线程竞争导致计数错误回收时直接遍历所有对象收集计数器为0的对象2.3 核心优点实现简单逻辑直观易于理解和编码实现判定效率高垃圾判定是O(1)操作无需遍历整个堆内存回收及时对象一旦变为垃圾计数为0可以立即被回收停顿时间短没有全局的堆扫描过程对应用程序的停顿影响小2.4 致命缺点循环引用问题这是引用计数法最严重的缺陷也是主流JVM不采用它的主要原因。示例classA{publicBb;}classB{publicAa;}publicclassTest{publicstaticvoidmain(String[]args){AanewA();// A对象计数1BbnewB();// B对象计数1a.bb;// B对象计数2b.aa;// A对象计数2anull;// A对象计数1bnull;// B对象计数1// 此时A和B对象已经无法被访问但它们的引用计数器都不为0// 引用计数法无法判定它们为垃圾导致内存泄漏}}2.5 其他缺点空间开销每个对象都需要额外的内存存储引用计数器时间开销频繁的引用赋值和失效会导致大量的计数器加减操作无法处理软引用、弱引用等特殊引用类型2.6 优化方案针对循环引用问题有一些优化思路但都存在局限性手动解除循环引用要求程序员在代码中显式断开循环引用容易出错且增加开发负担使用弱引用打破循环将循环中的一个引用改为弱引用当其他强引用消失时弱引用不会阻止对象被回收定期遍历检测循环定期扫描堆内存检测并回收循环引用的对象这会引入额外的开销且失去了引用计数法的高效性优势2.7 实际应用虽然主流JVMHotSpot、OpenJ9不使用引用计数法作为主要的垃圾判定算法但它在其他领域有广泛应用PythonCPython的垃圾回收机制结合了引用计数和分代回收PHP的垃圾回收机制Objective-C和Swift的ARC自动引用计数一些数据库和文件系统的资源管理三、可达性分析算法Reachability Analysis3.1 基本原理也称为根搜索算法是目前所有主流JVM采用的垃圾判定算法。核心思想以一系列称为GC Roots的对象作为起点从这些起点开始向下搜索搜索所走过的路径称为引用链Reference Chain。当一个对象到GC Roots没有任何引用链相连时即从GC Roots不可达则判定该对象为垃圾。3.2 GC Roots集合核心重点GC Roots是必须存活的对象它们永远不会被垃圾回收。在Java中可作为GC Roots的对象包括GC Roots类型具体说明虚拟机栈栈帧中的本地变量表中引用的对象方法执行时创建的局部变量、参数等引用的对象本地方法栈中JNINative方法引用的对象本地方法调用时使用的Java对象方法区中类静态属性引用的对象类的static变量引用的对象方法区中常量引用的对象字符串常量池、final常量等引用的对象同步锁synchronized关键字持有的对象作为锁的对象在被持有期间不能被回收JVM内部引用的对象如基本数据类型对应的Class对象、常驻异常对象、系统类加载器等反映JVM内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等JVM运行时需要的内部对象3.3 实现细节枚举GC RootsGC开始时需要暂停所有用户线程Stop-The-WorldSTW枚举所有GC Roots遍历引用链从GC Roots出发遍历所有可达的对象标记它们为存活清理未标记对象遍历完成后所有未被标记的对象即为垃圾3.4 核心优点解决了循环引用问题即使两个对象互相引用只要它们都不可达GC Roots就会被判定为垃圾准确性高能够准确识别所有真正的垃圾对象支持各种引用类型可以很好地处理强引用、软引用、弱引用、虚引用等不同强度的引用3.5 主要缺点需要STW停顿枚举GC Roots和遍历引用链时必须暂停所有用户线程这是GC停顿的主要来源之一时间开销大垃圾判定是O(n)操作堆内存越大存活对象越多遍历时间越长实现复杂需要处理各种复杂的引用关系和多线程场景3.6 优化方案为了减少STW时间和提高效率现代JVM对可达性分析算法进行了大量优化OopMapOrdinary Object Pointer Map记录栈帧和寄存器中哪些位置是对象引用避免全栈扫描安全点Safepoint让用户线程在特定的位置暂停而不是任意位置提高GC效率安全区域Safe Region解决线程处于Sleep或Blocked状态时无法到达安全点的问题并发标记在用户线程运行的同时进行标记减少STW时间如CMS、G1、ZGC等垃圾回收器3.7 实际应用所有主流JVM实现HotSpot、OpenJ9、GraalVM等C#的CLR垃圾回收机制Go语言的垃圾回收机制四、两种算法的全面对比对比维度引用计数法可达性分析算法核心思想基于对象被引用的次数基于对象到GC Roots的可达性循环引用处理无法处理会导致内存泄漏可以完美处理判定效率O(1)非常高O(n)与存活对象数量成正比STW停顿几乎没有必须有且停顿时间与堆大小相关空间开销每个对象需要存储计数器几乎没有额外空间开销时间开销频繁的计数器加减操作一次性的堆遍历操作实现复杂度简单复杂支持特殊引用类型不支持完全支持主流JVM采用否是五、扩展知识引用类型与垃圾判定的关系在Java中引用的强度从强到弱分为4种它们与垃圾判定算法密切相关5.1 强引用Strong Reference最常见的引用类型如Object obj new Object()只要强引用存在垃圾回收器永远不会回收被引用的对象可达性分析中强引用连接的对象都是存活的5.2 软引用SoftReference用来描述一些有用但非必需的对象在系统将要发生内存溢出之前会把这些对象列进回收范围进行二次回收适合实现缓存5.3 弱引用WeakReference用来描述非必需对象强度比软引用更弱当垃圾回收器工作时无论内存是否足够都会回收被弱引用关联的对象适合实现临时缓存5.4 虚引用PhantomReference最弱的一种引用类型无法通过虚引用获取对象实例唯一目的是在对象被回收时收到一个系统通知必须与引用队列ReferenceQueue一起使用注意引用计数法只能处理强引用而可达性分析算法可以根据不同的引用类型采取不同的回收策略。六、常见面试考点为什么主流JVM不使用引用计数法什么是GC Roots哪些对象可以作为GC Roots可达性分析算法为什么需要STW停顿如何解决循环引用问题四种引用类型的区别和应用场景是什么OopMap和安全点的作用是什么JVM垃圾判定算法 面试问答卡片可直接背诵一、基础核心题必背Q1什么是垃圾判定它在JVM垃圾回收中的作用是什么答案垃圾判定是JVM垃圾回收GC的第一步核心任务是准确识别堆内存中哪些对象已经死亡不再被任何途径使用。作用为后续垃圾回收器提供明确的回收目标避免误删存活对象导致程序崩溃同时确保真正的垃圾被及时回收释放内存。设计目标准确性不能误判、高效性不占用过多资源、低影响尽量减少对应用程序的停顿。Q2简述引用计数法的基本原理和优缺点。答案基本原理为每个对象添加一个引用计数器记录该对象被引用的次数。新引用指向对象 → 计数器1引用失效 → 计数器-1计数器0 → 判定为垃圾可立即回收核心优点实现简单逻辑直观判定效率极高O(1)操作回收及时对象死亡立即回收几乎没有全局STW停顿主要缺点致命缺陷无法处理循环引用会导致内存泄漏空间开销每个对象需额外内存存储计数器时间开销频繁的引用赋值会产生大量计数器加减操作不支持软引用、弱引用等特殊引用类型Q3引用计数法的致命缺陷是什么请举例说明。答案致命缺陷是无法处理循环引用即两个或多个对象互相引用但它们都没有被外部引用指向此时它们的引用计数器都不为0无法被判定为垃圾导致内存泄漏。示例classA{publicBb;}classB{publicAa;}publicclassTest{publicstaticvoidmain(String[]args){AanewA();// A计数1BbnewB();// B计数1a.bb;// B计数2b.aa;// A计数2anull;// A计数1bnull;// B计数1// 此时A和B已无法被外部访问但计数器都不为0// 引用计数法无法回收它们造成内存泄漏}}Q4简述可达性分析算法的基本原理。答案也称为根搜索算法是所有主流JVM采用的垃圾判定算法。核心思想以一系列称为GC Roots的根对象作为起点从这些起点开始向下搜索搜索走过的路径称为引用链。若一个对象到GC Roots没有任何引用链相连即从GC Roots不可达则判定该对象为垃圾。若一个对象通过引用链与GC Roots相连则判定为存活对象不会被回收。Q5什么是GC Roots哪些对象可以作为GC Roots高频必背答案GC Roots是必须存活的对象它们永远不会被垃圾回收是可达性分析的起点。可作为GC Roots的对象包括虚拟机栈栈帧本地变量表中引用的对象方法执行时的局部变量、参数等本地方法栈中JNINative方法引用的对象方法区中类静态属性引用的对象类的static变量方法区中常量引用的对象字符串常量池、final常量等同步锁synchronized持有的对象被作为锁的对象JVM内部引用的对象基本数据类型的Class对象、常驻异常对象、系统类加载器等其他JMXBean、JVMTI回调、本地代码缓存等二、进阶对比题Q6引用计数法和可达性分析算法的核心区别是什么答案对比维度引用计数法可达性分析算法核心思想基于对象被引用的次数基于对象到GC Roots的可达性循环引用处理无法处理导致内存泄漏完美处理判定效率O(1)极高O(n)与存活对象数量成正比STW停顿几乎没有必须有停顿时间与堆大小相关空间开销每个对象需存储计数器几乎无额外空间开销时间开销频繁的计数器加减一次性堆遍历实现复杂度简单复杂特殊引用支持不支持完全支持主流JVM采用否是Q7为什么主流JVM如HotSpot选择使用可达性分析算法而不是引用计数法答案根本原因是引用计数法存在无法解决的循环引用问题会导致严重的内存泄漏。此外还有以下原因引用计数法不支持Java的软引用、弱引用、虚引用等特殊引用类型无法实现灵活的内存管理策略现代JVM堆内存越来越大引用计数法的频繁计数器加减操作带来的开销会变得不可忽视可达性分析算法虽然有STW停顿但现代JVM通过OopMap、安全点、并发标记等技术已经大幅降低了停顿时间能够满足绝大多数应用的需求三、扩展优化题Q8可达性分析算法为什么需要Stop-The-WorldSTW停顿答案可达性分析必须在一致性快照下进行即分析过程中对象的引用关系不能发生变化。如果不暂停用户线程在分析过程中可能会出现一个对象刚被标记为垃圾又被其他线程引用导致该对象被错误回收程序崩溃或者一个对象刚被标记为存活又被所有线程引用失效导致该对象成为浮动垃圾本次GC无法回收因此枚举GC Roots和遍历引用链的核心阶段必须暂停所有用户线程这是GC停顿的主要来源之一。Q9HotSpot虚拟机为了优化可达性分析的效率做了哪些关键优化答案OopMap对象指针映射表记录栈帧和寄存器中哪些位置是对象引用避免GC时全栈扫描大幅加快GC Roots枚举速度安全点Safepoint规定用户线程只能在特定的安全点位置暂停安全点通常选在方法调用、循环跳转、异常跳转等指令处避免线程在任意位置暂停提高GC效率安全区域Safe Region解决线程处于Sleep或Blocked状态时无法到达安全点的问题当线程进入安全区域时会标记自己处于安全区域GC可以直接处理这些线程线程离开安全区域前必须检查GC是否完成并发标记在用户线程运行的同时进行大部分标记工作仅在初始标记和重新标记阶段需要短暂STW被CMS、G1、ZGC等现代垃圾回收器广泛采用Q10Java中的四种引用类型分别是什么它们的区别和应用场景是什么高频必背答案Java中引用强度从强到弱分为4种只有可达性分析算法支持所有引用类型引用类型强度回收时机应用场景强引用最强只要强引用存在永远不会回收最常见的引用如Object obj new Object()软引用SoftReference次强系统将要发生内存溢出之前进行二次回收实现内存敏感的缓存如图片缓存弱引用WeakReference较弱垃圾回收器工作时无论内存是否足够都会回收实现临时缓存、ThreadLocal内部实现虚引用PhantomReference最弱无法通过虚引用获取对象仅在对象被回收时收到通知跟踪对象回收状态管理堆外内存注意引用计数法只能处理强引用无法区分其他三种引用类型。背诵小贴士先理解原理再背诵不要死记硬背重点记忆GC Roots类型和四种引用类型这是面试最高频考点对比记忆两种算法的优缺点特别是循环引用问题结合例子理解抽象概念如循环引用的示例面试时先答核心要点再根据面试官的追问展开细节JVM垃圾判定算法 一页纸速记版考前必看一、基础核心优先级★★★★★垃圾判定GC第一步识别堆中死亡对象目标准确、高效、低影响引用计数法原理对象头存计数器引用1失效-10即垃圾优点O(1)判定回收及时几乎无STW致命缺陷无法处理循环引用→内存泄漏其他缺点空间/时间开销不支持特殊引用可达性分析算法主流JVM采用原理以GC Roots为起点无引用链相连即垃圾GC Roots高频必背虚拟机栈引用本地方法栈JNI引用方法区静态属性/常量引用同步锁持有对象JVM内部对象二、进阶对比优先级★★★★☆维度引用计数法可达性分析核心引用次数GC Roots可达性循环引用❌ 无法处理✅ 完美处理效率O(1)O(n)与存活对象数成正比STW几乎无必须有特殊引用❌ 不支持✅ 完全支持核心结论主流JVM选可达性分析→根本原因是引用计数法无法解决循环引用三、扩展优化优先级★★★☆☆STW原因必须在一致性快照下分析避免引用关系变化导致误判HotSpot关键优化OopMap记录对象引用位置避免全栈扫描安全点线程只能在特定位置暂停安全区域解决Sleep/Blocked线程无法到达安全点问题并发标记用户线程运行时标记仅短时间STW四种引用类型高频必背强引用永不回收最常见软引用OOM前回收内存敏感缓存弱引用GC即回收临时缓存、ThreadLocal虚引用无法获取对象回收通知、堆外内存管理背诵优先级第一梯队GC Roots列表、四种引用类型、循环引用问题第二梯队两种算法核心对比、STW原因第三梯队HotSpot优化细节