Appearance
垃圾回收
垃圾回收概述
┌─────────────────────────────────────────────────────────────────┐
│ 垃圾回收流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. 标记阶段 │
│ - 引用计数法(Python使用,Java不用) │
│ - 可达性分析法(Java使用) │
│ ↓ │
│ 2. 回收阶段 │
│ - 标记-清除算法 │
│ - 复制算法 │
│ - 标记-整理算法 │
│ ↓ │
│ 3. 分代回收 │
│ - 新生代(Minor GC) │
│ - 老年代(Major GC / Full GC) │
│ │
└─────────────────────────────────────────────────────────────────┘引用类型
引用分类
java
// 1. 强引用(Strong Reference)
Object obj = new Object(); // 永远不会被回收
obj = null; // 变为垃圾
// 2. 软引用(Soft Reference)
SoftReference<byte[]> cache = new SoftReference<>(new byte[1024*1024]);
// 内存不足时回收
// 3. 弱引用(Weak Reference)
WeakReference<Object> weak = new WeakReference<>(new Object());
// 下次 GC 时回收
// 4. 虚引用(Phantom Reference)
ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> phantom = new PhantomReference<>(new Object(), queue);
// 用于跟踪对象被回收的状态引用强度对比
强引用 > 软引用 > 弱引用 > 虚引用
┌─────────────────────────────────────────────────────────────────┐
│ 强引用 - Object obj = new Object() 永不回收 │
│ 软引用 - SoftReference 内存不足时回收 │
│ 弱引用 - WeakReference 下次 GC 必定回收 │
│ 虚引用 - PhantomReference 形同虚设,回收通知 │
└─────────────────────────────────────────────────────────────────┘垃圾收集算法
标记-清除算法
┌─────────────────────────────────────────────────────────────────┐
│ 标记-清除算法 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 标记阶段: │
│ ┌────┬────┬────┬────┬────┬────┐ │
│ │ √ │ │ √ │ │ √ │ │ √ = 存活对象 │
│ └────┴────┴────┴────┴────┴────┘ │
│ │
│ 清除阶段: │
│ ┌────┬ ┌────┬ ┌────┬ │ 留下空洞 │
│ │ │ │ │ │ │ │ │
│ └────┴────┴────┴────┴────┴────┘ │
│ │
│ 缺点: 产生内存碎片 │
│ │
└─────────────────────────────────────────────────────────────────┘复制算法
┌─────────────────────────────────────────────────────────────────┐
│ 复制算法 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 内存分两块: │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ From Space │ ──▶ │ To Space │ │
│ │ (使用中) │ │ (空闲) │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ 存活对象复制到 To Space,然后清空 From Space │
│ │
│ 优点: 无碎片,简单高效 │
│ 缺点: 浪费一半空间 │
│ │
└─────────────────────────────────────────────────────────────────┘标记-整理算法
┌─────────────────────────────────────────────────────────────────┐
│ 标记-整理算法 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 标记阶段: │
│ ┌────┬────┬────┬────┬────┬────┐ │
│ │ √ │ │ √ │ │ √ │ │ │
│ └────┴────┴────┴────┴────┴────┘ │
│ │
│ 整理阶段: │
│ ┌────┬────┬────┬────┬────┬────┐ │
│ │ √ │ √ │ √ │ │ │ │ 紧凑排列 │
│ └────┴────┴────┴────┴────┴────┘ │
│ │
│ 优点: 无碎片 │
│ 缺点: 移动对象,开销较大 │
│ │
└─────────────────────────────────────────────────────────────────┘分代收集
分代假设
┌─────────────────────────────────────────────────────────────────┐
│ 分代假说 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. 弱分代假说: 大多数对象都是朝生夕死 │
│ │
│ 2. 强分代假说: 熬过多次 GC 的对象越难死亡 │
│ │
│ 3. 跨代引用假说: 新生代对象可能被老年代引用 │
│ │
└─────────────────────────────────────────────────────────────────┘回收策略
┌─────────────────────────────────────────────────────────────────┐
│ 分代回收策略 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 新生代 (Young Gen) │
│ ├── Eden Space (8/10) │
│ └── Survivor Space (1/10 × 2) │
│ ↑ │
│ │ Minor GC │
│ │ │
│ ▼ │
│ 老年代 (Old Gen) │
│ │ │
│ │ Major GC / Full GC │
│ │ │
│ ▼ │
│ 永久代/元空间 (方法区) │
│ │ │
│ │ Full GC │
│ │ │
│ ▼ │
│ │
└─────────────────────────────────────────────────────────────────┘GC 类型
| 类型 | 发生区域 | 触发条件 | 备注 |
|---|---|---|---|
| Minor GC | 新生代 | Eden 满 | 频率高,停顿短 |
| Major GC | 老年代 | 老年代满 | 频率低,停顿长 |
| Full GC | 全堆+方法区 | 多种条件 | 停顿最长 |
垃圾收集器
收集器对比
┌─────────────────────────────────────────────────────────────────┐
│ 垃圾收集器对比 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 新生代收集器: │
│ - Serial (单线程,Client模式) │
│ - ParNew (多线程,Server模式,CMS搭档) │
│ - Parallel Scavenge (吞吐量优先) │
│ │
│ 老年代收集器: │
│ - Serial Old (单线程) │
│ - Parallel Old (多线程) │
│ - CMS (并发标记清除,追求低停顿) │
│ │
│ 不分代收集器: │
│ - G1 (JDK 9+ 默认) │
│ - ZGC (大内存,低停顿) │
│ - Shenandoah │
│ │
└─────────────────────────────────────────────────────────────────┘常用收集器组合
| 组合 | 新生代 | 老年代 | 说明 |
|---|---|---|---|
| Serial + Serial Old | Serial | Serial Old | Client 模式 |
| ParNew + CMS | ParNew | CMS | 追求低停顿 |
| Parallel Scavenge + Parallel Old | Parallel Scavenge | Parallel Old | 追求高吞吐 |
| G1 | G1 | G1 | JDK 9+ 默认 |
| ZGC + ZGC | ZGC | ZGC | 超大内存 |
CMS 收集器
┌─────────────────────────────────────────────────────────────────┐
│ CMS 收集器工作流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. 初始标记 (STW) - 标记 GC Root 直接引用的对象 │
│ ↓ │
│ 2. 并发标记 - 遍历对象图 │
│ ↓ │
│ 3. 重新标记 (STW) - 修正并发标记期间变动 │
│ ↓ │
│ 4. 并发清除 - 清除死亡对象 │
│ │
│ 优点: 并发收集,低停顿 │
│ 缺点: 产生内存碎片,CPU敏感 │
│ │
└─────────────────────────────────────────────────────────────────┘G1 收集器
┌─────────────────────────────────────────────────────────────────┐
│ G1 收集器原理 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ G1 把堆分成多个大小相等的 Region: │
│ │
│ ┌──────┬──────┬──────┬──────┐ │
│ │ Eden │ Eden │ S │ Eden │ │
│ ├──────┼──────┴──────┼──────┤ │
│ │ S │ Old │ S │ S = Survivor │
│ ├──────┼─────────────┼──────┤ │
│ │ H │ Old │ H │ H = Humongous (大对象) │
│ └──────┴─────────────┴──────┘ │
│ │
│ - 并行与并发 │
│ - 分代收集 │
│ - 空间整合(无碎片) │
│ - 可预测停顿(-XX:MaxGCPauseMillis) │
│ │
└─────────────────────────────────────────────────────────────────┘GC 日志分析
日志参数
bash
# 打印 GC 日志
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
# 输出到文件
-Xloggc:/path/to/gc.log
# 打印 Full GC 前打印年轻代信息
-XX:+PrintHeapAtGCGC 日志解读
2024-01-15T10:30:45.123+0800: [GC (Allocation Failure)
[PSYoungGen: 1024K->128K(1536K)] 2048K->512K(4096K)
0.015ms]| 字段 | 说明 |
|---|---|
| Allocation Failure | 触发原因:对象分配失败 |
| PSYoungGen | 新生代垃圾收集器 |
| 1024K->128K | 收集前->收集后 |
| (1536K) | 总容量 |
| 2048K->512K | 堆内存使用 2M->512K |
| (4096K) | 堆总容量 |
总结
垃圾回收核心知识点:
| 知识点 | 面试频率 | 实战重要性 |
|---|---|---|
| 引用类型(强软弱虚) | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 垃圾收集算法 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 分代收集原理 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 垃圾收集器 | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| GC 日志分析 | ⭐⭐⭐ | ⭐⭐⭐⭐ |
⚠️ 易错点提醒:
- Java 不用引用计数法(无法处理循环引用)
- 复制算法用于新生代,标记-整理用于老年代
- Minor GC 比 Major/Full GC 更频繁
- CMS 会产生内存碎片,需要配合 Full GC 整理
- G1 是 JDK 9+ 默认收集器