Skip to content

垃圾回收

垃圾回收概述

┌─────────────────────────────────────────────────────────────────┐
│                        垃圾回收流程                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   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 OldSerialSerial OldClient 模式
ParNew + CMSParNewCMS追求低停顿
Parallel Scavenge + Parallel OldParallel ScavengeParallel Old追求高吞吐
G1G1G1JDK 9+ 默认
ZGC + ZGCZGCZGC超大内存

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:+PrintHeapAtGC

GC 日志解读

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 日志分析⭐⭐⭐⭐⭐⭐⭐

⚠️ 易错点提醒

  1. Java 不用引用计数法(无法处理循环引用)
  2. 复制算法用于新生代,标记-整理用于老年代
  3. Minor GC 比 Major/Full GC 更频繁
  4. CMS 会产生内存碎片,需要配合 Full GC 整理
  5. G1 是 JDK 9+ 默认收集器