JVM GC 机制详解:算法、分代、回收器、GC 类型一篇讲透 前言JVM 的垃圾回收是 Java 面试里的高频重点也是线上问题排查时绕不开的一块。很多人学 GC 的时候容易陷入两个误区只背概念不知道这些机制为什么存在只记回收器名字不知道它们到底解决了什么问题这篇笔记的目标不是把 GC 讲得特别“学术”而是帮你建立一套完整的理解框架先知道 JVM 为什么需要 GC再理解对象怎么判断存活然后把回收算法、分代思想、回收器和 GC 类型串起来。一、为什么 JVM 需要垃圾回收Java 和 C、C 最大的一个区别就是 Java 不需要程序员手动释放内存。对象创建后JVM 会负责管理它的生命周期。当一个对象已经不再被程序使用时就应该把它占用的内存回收掉否则对象越积越多最终就会导致内存溢出。所以 GC 的本质就是一句话自动找出“已经没用的对象”并把它们占用的内存清理掉。这样做的好处是降低手动管理内存的复杂度减少野指针、重复释放等问题让开发者更专注于业务逻辑但代价也很明显GC 会占用 CPU 资源GC 期间可能发生停顿如果内存分配和回收策略不合理性能会明显抖动二、先补一个背景JVM 运行时内存区域在讲 GC 前先明确一件事不是 JVM 所有内存区域都会发生垃圾回收GC 重点关注的是堆和方法区。JVM 运行时内存大致可以分成下面几块1. 程序计数器线程私有记录当前线程执行到哪条字节码指令。这个区域很小也基本不涉及 GC。2. 虚拟机栈线程私有存放方法调用过程中的栈帧、局部变量表等。方法执行结束对应栈帧出栈内存自然释放也不属于典型 GC 处理范围。3. 本地方法栈和虚拟机栈类似只不过服务的是 Native 方法。4. 堆堆是 Java 对象实例最主要的存储区域也是垃圾回收最核心的区域。5. 方法区方法区主要存储类元信息、常量、静态变量、JIT 编译后的代码缓存等。HotSpot 里早期常提永久代后来更多提元空间。一句话总结GC 主要回收的是堆中的对象以及方法区中不再使用的类和常量。三、如何判断对象已经“死了”垃圾回收的第一步不是回收而是判断哪些对象还能用哪些对象已经没用了。1. 引用计数法引用计数法的思路很直观给对象加一个引用计数器每被一个地方引用计数器加一每失去一个引用计数器减一当计数器变成 0就说明对象可以回收这个方法看起来很简单但有一个致命问题它无法很好地解决对象循环引用的问题。比如 A 引用 BB 也引用 A虽然这两个对象其实都已经不再被外部使用但它们的计数器都不为 0就无法回收。2. 可达性分析JVM 主流实现采用的是可达性分析算法。它的核心思想是从一组叫作 GC Roots 的对象作为起点往下搜索能被这些起点直接或间接关联到的对象就是“存活对象”反之就是可回收对象。常见的 GC Roots 包括虚拟机栈中引用的对象方法区中类静态属性引用的对象方法区中常量引用的对象本地方法栈 JNI 引用的对象被同步锁持有的对象所以判断对象是否存活不是看“有没有引用”而是看它是否还能从 GC Roots 追踪到。四、Java 中几种常见引用类型Java 里不只有“有引用”和“没引用”两种状态还进一步细分了引用强度。1. 强引用最普通的引用就是强引用比如Object obj new Object();只要强引用还在GC 一般就不会回收这个对象。2. 软引用软引用用于描述那些“有用但不是必须”的对象。内存不足时JVM 会优先回收软引用对象。典型场景是缓存。3. 弱引用弱引用比软引用更弱。只要发生 GC不管当前内存紧不紧张弱引用关联的对象都可能被回收。典型场景是 ThreadLocalMap 中的 Key。4. 虚引用虚引用几乎不影响对象生命周期主要用来在对象被回收时收到通知常用于更底层的资源管理。一句话理解强引用最稳软引用适合缓存弱引用更容易被回收虚引用主要是配合回收通知。五、垃圾回收的基础算法有哪些这部分是 GC 的底层核心。后面的分代回收器本质上都是在这些基础算法上做组合和优化。1. 标记-清除算法流程分两步先标记出所有需要回收的对象再统一清除这些对象优点是实现简单。缺点主要有两个效率一般会产生内存碎片内存碎片的意思是虽然总空闲内存够但它们不连续大对象可能没法直接分配。2. 复制算法复制算法的思路是把内存分成两块每次只使用其中一块发生 GC 时把存活对象复制到另一块然后一次性清空原来的那一整块优点实现简单没有内存碎片适合“存活对象少、死亡对象多”的场景缺点需要额外预留一部分空间如果存活对象很多复制成本高3. 标记-整理算法标记-整理可以看成标记-清除的改良版。流程是标记存活对象让所有存活对象向一端移动清理边界之外的内存优点是没有内存碎片适合老年代这种对象存活率较高的场景缺点是整理过程有移动成本4. 分代收集算法严格来说分代收集不是一个基础算法而是一种设计思想。它的核心判断是不同生命周期的对象适合不同的回收策略。因为大多数 Java 对象都“朝生夕死”少数对象会长期存活所以 JVM 把堆划分为不同区域针对不同区域采用不同算法这就是分代回收思想。六、为什么要分代回收分代回收的本质依据是对象生命周期特征不同。大多数对象都有两个典型特点刚创建出来很快就没用了少量对象会一直活很久所以 JVM 通常会把堆分成新生代老年代有些资料还会额外提永久代或元空间但那更多属于方法区的实现不是传统意义上的“堆分代”。1. 新生代新生代主要放新创建的对象。这里对象数量多但大部分死得快所以适合使用复制算法。2. 老年代老年代主要放存活时间较长的对象。这里对象存活率高不适合简单复制所以更常采用标记-清除或标记-整理思想。一句话总结新生代追求“快速回收大量短命对象”老年代关注“稳定回收长期存活对象”。七、新生代为什么分 Eden、Survivor新生代一般不是一整块连续区域而是进一步分成Eden 区Survivor From 区Survivor To 区默认情况下大部分对象会先在 Eden 里分配。当 Eden 空间不够时会触发一次 Minor GC。GC 后还活着的对象会被复制到 Survivor 区。之后每经历一次回收还活着对象年龄就会加一。当对象年龄达到某个阈值后就会晋升到老年代。这里常说的新生代比例一般是Eden : Survivor From : Survivor To 8 : 1 : 1这样设计的原因是大多数对象都死得很快不需要给 Survivor 留太大空间保留两个 Survivor 区方便复制和年龄统计八、对象什么时候进入老年代对象进入老年代常见有几种情况1. 年龄达到阈值对象每在 Survivor 区熬过一次 GC年龄就会加一。达到晋升阈值后进入老年代。2. 大对象直接进入老年代如果对象特别大比如超大的数组为了避免在 Eden 和 Survivor 之间来回复制JVM 可能会让它直接进入老年代。3. 动态年龄判断如果 Survivor 中相同年龄对象的总大小超过了某个比例不一定非得等到设定阈值也可能提前晋升老年代。九、GC 的几种常见类型这部分面试里很高频最好能准确区分。1. Minor GC也叫 Young GC发生在新生代。特点频率高回收速度快停顿时间通常较短因为新生代大部分对象都会在一次次 Minor GC 中被直接清掉。2. Major GCMajor GC 一般指针对老年代的 GC。但要注意在不同资料和不同实现里Major GC 的叫法并不总是完全统一。所以面试里更稳妥的说法是Minor GC新生代回收Full GC整个堆甚至连方法区一起回收3. Full GCFull GC 往往会回收新生代老年代方法区Full GC 的特点通常是停顿时间长对系统影响大应尽量避免频繁发生4. Mixed GCMixed GC 是 G1 里比较典型的概念。它不是只回收新生代也不是整个堆全回收而是在回收新生代的同时选择一部分收益高的老年代 Region 一起回收。这样既能控制停顿又能逐步回收老年代。十、经典垃圾回收器怎么理解回收算法解决的是“怎么回收”回收器解决的是“谁来执行这些算法以及怎么权衡吞吐量、停顿时间和资源消耗”。十一、Serial 回收器Serial 是最基础、最简单的收集器。特点单线程回收发生 GC 时必须 Stop-The-World实现简单额外开销小它适合单核环境Client 模式内存不大、对停顿不敏感的场景它的缺点也很明显一旦堆大、并发高停顿会比较明显十二、ParNew 回收器ParNew 可以理解成 Serial 的多线程版本主要工作在新生代。特点多线程并行回收仍然会 STW在多核机器上比 Serial 更高效它过去经常和 CMS 搭配使用。十三、Parallel Scavenge 回收器Parallel Scavenge 也是新生代回收器重点不是尽量缩短单次停顿而是追求高吞吐量。这里的吞吐量可以简单理解为用户代码执行时间 /用户代码执行时间 垃圾回收时间所以它适合后台计算批处理任务更看重整体吞吐而不是单次响应时间的场景它常和 Parallel Old 搭配。十四、Serial Old 和 Parallel Old这两个都是老年代回收器。1. Serial OldSerial Old 是 Serial 的老年代版本单线程通常采用标记-整理算法。2. Parallel OldParallel Old 是 Parallel Scavenge 的老年代搭档多线程关注吞吐量。如果应用更关注整体处理能力而不是响应延迟这套组合会比较常见。十五、CMS 回收器CMS全称 Concurrent Mark Sweep是一款以低停顿为目标的老年代回收器。它的核心思想是尽量让垃圾回收和用户线程并发执行减少停顿时间。CMS 的主要过程一般包括初始标记并发标记重新标记并发清除其中初始标记和重新标记需要 STW并发标记和并发清除可以和用户线程同时进行CMS 的优点是停顿时间短对响应时间敏感的系统比较友好CMS 的缺点也很典型会产生内存碎片对 CPU 资源敏感并发阶段如果回收跟不上分配速度可能触发 Concurrent Mode Failure后期已经被更新的回收器逐步替代一句话总结CMS 的核心价值是低停顿但它用空间碎片和更复杂的运行成本换来了这个优势。十六、G1 回收器G1Garbage First是现在非常主流的一款服务端垃圾回收器。它和传统分代回收器一个很大的区别在于G1 不再强调固定连续的新生代、老年代物理边界而是把整个堆划分成多个大小相等的 Region。这些 Region 在逻辑上仍然可以扮演 Eden、Survivor、Old 等角色。G1 的核心特点1. Region 化把堆拆成很多小块而不是简单地分成一整块新生代和一整块老年代。2. 可预测停顿G1 会根据回收收益优先选择垃圾最多、回收价值最高的 Region这也是 “Garbage First” 名字的来源。3. Mixed GCG1 不需要每次都 Full GC 才处理老年代而是可以在回收新生代时顺带挑一些老年代 Region 一起回收。G1 的适用场景大堆内存多核服务器既希望吞吐不错又希望停顿尽量可控G1 的不足实现复杂在某些极端场景下未必总比 CMS 或 Parallel 更有优势一句话总结G1 的核心不是“绝对最快”而是“在大堆场景下尽量把停顿控制得更可预测”。十七、ZGC 和 Shenandoah 简单了解如果面试官问到新一代低延迟 GC可以简单提一下 ZGC 和 Shenandoah。1. ZGCZGC 的目标是把停顿时间压得非常低哪怕堆很大也尽量保持低停顿。特点面向超大堆停顿时间极短更多通过并发阶段完成大部分工作2. ShenandoahShenandoah 也是低停顿回收器设计目标和 ZGC 类似也强调尽量并发执行回收过程。如果只是常规面试知道到这里通常就够了。除非岗位明确对 JVM 调优要求很高否则一般不会深挖底层实现细节。十八、什么是 Stop-The-WorldStop-The-World简称 STW指的是在 GC 的某些关键阶段JVM 必须暂停所有用户线程只让垃圾回收线程工作。这也是为什么 GC 会影响响应时间的根本原因。需要注意的是不是只有 Full GC 才会 STWMinor GC 也可能 STW并发回收器也不是完全不停顿只是尽量缩短停顿时间所以判断一个回收器好不好很多时候看的就是停顿多久停顿是否可控吞吐量损失大不大十九、对象一定会被立刻回收吗不会。一个对象即使已经不可达也不代表它会在下一秒立刻被清理。GC 是按一定时机触发的具体什么时候回收要看当前内存分配情况GC 触发条件使用的回收器JVM 参数配置所以更准确的理解应该是“可回收”不等于“立刻回收”而是说明这个对象已经具备了被 GC 清理的资格。二十、什么情况下容易频繁 Full GC这类题既能用于面试也能用于排查线上问题。常见原因包括1. 老年代空间不足对象晋升太快或者老年代回收速度跟不上分配速度都会导致 Full GC。2. 大对象分配过多大对象直接进入老年代容易快速把老年代打满。3. 内存泄漏对象其实已经不该用了但仍然被引用着导致 GC 无法回收。4. 元空间不足如果频繁动态生成类比如大量反射、代理、热部署不当也可能导致方法区相关 Full GC。5. 参数配置不合理比如新生代太小、晋升阈值不合适、堆大小配置不合理也会让 Full GC 更频繁。二十一、如何理解内存泄漏和内存溢出这两个概念很容易被混用。1. 内存泄漏内存泄漏指的是对象本来已经没用了但仍然被某些引用持有导致 GC 无法回收。2. 内存溢出内存溢出指的是JVM 已经没有足够内存完成新的分配请求最终抛出 OOM。关系是内存泄漏不一定立刻 OOM但严重的内存泄漏经常最终会导致内存溢出二十二、面试里怎么讲“为什么会有分代”这是一个很值得背的表达。可以直接这样说JVM 之所以采用分代回收核心原因是不同对象的生命周期差异很大。大多数对象朝生夕死适合放在新生代用复制算法快速清理少数对象长期存活适合放在老年代用标记-清除或标记-整理进行回收。这样做能显著提高 GC 效率避免所有对象都用同一种回收策略。二十三、面试里怎么讲“G1 和 CMS 的区别”可以按下面这个思路回答CMS 更强调低停顿主要针对老年代核心问题是会产生内存碎片而且在高并发下可能出现并发失败G1 则把堆拆成多个 Region能够在回收新生代的同时回收部分老年代并且更强调停顿时间的可预测性。简单来说CMS 是传统低停顿方案G1 是更现代、更通用的大堆低停顿方案。二十四、面试里怎么讲“GC 算法和回收器的关系”这也是很容易被问到的一个点。可以这样理解GC 算法回答的是“垃圾怎么回收”垃圾回收器回答的是“具体由谁、以什么方式执行这些算法”比如Serial、ParNew、G1、CMS 是回收器标记-清除、复制、标记-整理是算法一句话总结算法是理论基础回收器是工程实现。二十五、最后给一版适合背诵的总结如果面试官问“你怎么理解 JVM 的 GC 机制”可以直接按下面这段说JVM 的 GC 机制本质上就是自动识别无用对象并回收内存。判断对象是否存活主流是通过可达性分析从 GC Roots 出发去查找可达对象。真正执行回收时底层会用到标记-清除、复制、标记-整理这些算法。由于大多数对象生命周期都很短所以 JVM 引入了分代回收思想把堆分成新生代和老年代新生代对象死亡率高通常用复制算法老年代对象存活率高更适合标记-清除或标记-整理。常见 GC 类型包括 Minor GC、Major GC、Full GCG1 里还有 Mixed GC。回收器层面早期有 Serial、ParNew、Parallel、CMS现在更主流的是 G1更低停顿场景下还有 ZGC、Shenandoah。理解 GC 的关键不是死记名字而是知道每种机制背后在解决什么问题比如吞吐量、停顿时间、碎片整理和大堆场景下的可控性。