JVM垃圾回收与调优核心面试笔记(引用计数/GC算法/CMS/G1/参数调优)
文章标签:#jvm #gc #面试 #java #后端
前言
本文为JVM垃圾回收核心复习笔记,覆盖对象存活判定、三大GC算法、CMS执行流程、G1分区机制、JVM参数调优全链路考点,适配面试突击与实战复盘,逻辑对标HashMap笔记风格,便于快速记忆与背诵。
一、对象存活判定算法:引用计数 & 可达性分析
GC第一步:判断对象是否"死亡",主流两种算法,HotSpot仅用后者。
1.1 引用计数法(Reference Counting)
- 原理:为每个对象维护引用计数器,有引用+1,引用失效-1,计数器=0则可回收。
- 优点:实现简单、判断高效、可立即回收。
- 致命缺陷 :无法解决循环引用,导致内存泄漏。
java
// 循环引用示例(引用计数无法回收)
public class CircularRef {
Object instance;
public static void main(String[] args) {
CircularRef a = new CircularRef();
CircularRef b = new CircularRef();
a.instance = b;
b.instance = a;
a = null; b = null; // 无外部引用,但计数均为1
}
}
- 结论:HotSpot未采用,仅作历史了解。
1.2 可达性分析算法(主流)
- 核心思想 :以GC Roots 为起点,递归遍历引用链,不可达对象=垃圾。
- GC Roots固定范围(必背) :
- 虚拟机栈局部变量表引用的对象
- 方法区静态属性引用的对象
- 方法区常量引用的对象
- 本地方法栈JNI引用的对象
- 活跃线程对象
- 流程:STW暂停→从GC Roots遍历→标记可达对象→回收不可达对象。
- 优势:解决循环引用、精准判定、适配JVM运行模型。
二、三大基础GC算法:复制 / 标记清除 / 标记整理
2.1 复制算法(Copying)
- 核心 :内存分等大两块,每次只用一块,满时将存活对象复制到另一块,清空原块。
- 优点:无碎片、复制高效、适合存活对象少的场景。
- 缺点:内存利用率仅50%,存活对象多则效率暴跌。
- 适用 :新生代(Eden+Survivor,8:1:1),Minor GC默认用此算法。
2.2 标记-清除算法(Mark-Sweep)
- 流程:先标记所有存活对象→统一清除未标记垃圾。
- 优点:无需移动对象、实现简单。
- 缺点 :内存碎片严重、效率随垃圾数量波动。
- 适用:老年代早期方案,CMS底层算法。
2.3 标记-整理算法(Mark-Compact)
- 流程 :标记存活→将存活对象向一端压缩→清除端外内存。
- 优点:无碎片、内存规整。
- 缺点:需移动对象、全程STW、效率偏低。
- 适用:老年代(Parallel Old、Serial Old)。
三大算法对比表
| 算法 | 效率 | 内存碎片 | 移动对象 | 适用代 |
|---|---|---|---|---|
| 复制 | 高 | 无 | 是 | 新生代 |
| 标记-清除 | 中 | 严重 | 否 | 老年代(CMS) |
| 标记-整理 | 低 | 无 | 是 | 老年代 |
三、CMS 执行流程与优缺点
CMS(Concurrent Mark Sweep):低延迟老年代收集器 ,以最短STW为目标,面试高频。
3.1 核心流程(4阶段,2次STW)
- 初始标记(Initial Mark)STW
仅标记GC Roots直接关联对象,速度极快。 - 并发标记(Concurrent Mark)并发
与业务线程并行,遍历全引用链标记存活对象,耗时最长但无停顿。 - 重新标记(Remark)STW
修正并发标记期间引用变动导致的漏标,停顿短于Full GC。 - 并发清除(Concurrent Sweep)并发
异步回收垃圾,不移动对象,业务线程可继续运行。
3.2 优点
- 大部分阶段并发,停顿极低,适配响应敏感型服务。
- 不移动对象,降低STW开销。
3.3 缺点(必背)
- CPU敏感:并发阶段抢占算力,核心数<4时吞吐量暴跌。
- 浮动垃圾:并发清除时新产生垃圾无法当次回收,需预留内存。
- 内存碎片:基于标记-清除,碎片多易触发Full GC。
- 并发失败:内存不足退化为Serial Old,停顿剧增。
四、G1 核心思想与分区机制
G1(Garbage-First):服务端大堆首选 ,平衡停顿可控+高回收效率,JDK9+默认收集器。
4.1 核心思想
- 化整为零 :堆拆分为独立Region,按需回收,不扫描全堆。
- 垃圾优先 :优先回收垃圾最多、收益最大的Region,故名G1。
- 停顿可预测 :通过
-XX:MaxGCPauseMillis指定目标,动态调整回收量。
4.2 分区机制(Region)
- 堆划分 :约2048个等大Region,大小1MB~32MB(2的幂)。
- 动态角色 :每个Region可充当Eden/Survivor/Old/Humongous(巨型对象),逻辑分代、物理不连续。
- Humongous Region:存储>1.5个Region的大对象,避免大对象分配失败。
- Remembered Set:记录跨Region引用,避免全局扫描,加速回收。
4.3 G1回收模式
- Young GC:回收新生代Region,复制算法,STW可控。
- Mixed GC :回收新生代+高收益老年代Region,不回收全老年代。
- Full GC :并发失败触发,单线程收集,需避免。
五、JVM 常用参数调优(生产实战版)
按内存、GC、监控、最佳实践分类,直接复制可用。
5.1 堆内存核心参数
-Xms8g -Xmx8g # 初始堆=最大堆,禁止扩容震荡
-Xmn3g # 新生代大小,推荐堆1/3~1/2
-XX:SurvivorRatio=8 # Eden:S0:S1=8:1:1
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m # 元空间
-Xss1m # 线程栈,默认1MB
5.2 GC收集器参数
-XX:+UseG1GC # 生产首选G1
-XX:MaxGCPauseMillis=200 # G1停顿目标ms
-XX:+UseConcMarkSweepGC # CMS启用(老项目)
-XX:+CMSScavengeBeforeRemark # CMS重新标记前先Young GC,缩短STW
-XX:ParallelGCThreads=8 # GC线程数≈CPU核心数
5.3 监控与故障定位
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dump/ # OOM自动dump
-XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps # GC日志
-Xlog:gc*:file=gc.log # JDK9+日志
-XX:+DisableExplicitGC # 禁用System.gc()
5.4 调优最佳实践
- Xms=Xmx,避免堆动态扩容。
- 新生代不宜过小,防止频繁Minor GC与过早晋升。
- G1优先于CMS,大堆(>8G)必用G1。
- 开启OOM dump,定位泄漏必备。
- 禁用
System.gc(),避免突发Full GC。
总结(面试速背版)
- 对象存活:引用计数(循环引用BUG) 、可达性分析(HotSpot标准)。
- GC算法:复制(新生代)、标记清除(CMS)、标记整理(老年代)。
- CMS:4阶段2次STW,低延迟但有碎片、浮动垃圾、CPU敏感。
- G1:Region分区、垃圾优先、停顿可控,生产首选。
- 调优:固定堆、合理新生代、选对收集器、开启监控。
需要我把本文整理成一页A4面试背诵清单(纯要点+口诀)吗?