全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-11 8 分钟 ✍️ juanwangdev

Java垃圾回收机制与算法

垃圾回收(GC)自动回收不再使用的对象,释放内存。

垃圾判断方法

1. 引用计数法

Java
// 对象被引用时计数+1,引用失效时-1
// 计数为0时可回收
// 问题:循环引用无法回收

2. 可达性分析(JVM使用)

从GC Roots开始搜索,不可达的对象可回收。

GC Roots包括

  • 栈中引用的对象
  • 方法区静态属性引用的对象
  • 方法区常量引用的对象
  • 本地方法栈JNI引用的对象
Java
        GC Roots
            ↓
      ┌─────┴─────┐
      │           │
    对象A        对象B
      ↓           ↓
    对象C        对象D
                  ↓
                对象E  ← 不可达,可回收

Java引用类型

类型描述GC行为
强引用new创建不会被回收
软引用SoftReference内存不足时回收
弱引用WeakReferenceGC时回收
虚引用PhantomReference无法获取对象,用于跟踪回收
Bash
// 强引用
Object obj = new Object();  // 不会被回收

// 软引用
SoftReference<Object> soft = new SoftReference<>(new Object());

// 弱引用
WeakReference<Object> weak = new WeakReference<>(new Object());

// 虚引用
PhantomReference<Object> phantom = new PhantomReference<>(new Object(), queue);

GC算法

1. 标记-清除

Bash
标记阶段:标记需要回收的对象
清除阶段:回收标记的对象

缺点:
- 效率不高
- 内存碎片
text
标记前:│A│B│C│D│E│F│
标记后:│A│ │C│ │E│F│  (B、D标记为垃圾)
清除后:│A│空│C│空│E│F│  (产生碎片)

2. 标记-复制

text
年轻代使用:
- Eden + Survivor0 + Survivor1
- Eden满时,存活对象复制到Survivor
- Survivor满时,复制到另一个Survivor或老年代

优点:无碎片
缺点:空间利用率低
text
Eden区:│A│B│C│垃圾│垃圾│
         ↓ 复制
Survivor:│A│B│C│空│空│

3. 标记-整理

text
标记阶段:标记存活对象
整理阶段:存活对象向一端移动,清理边界内存

优点:无碎片
缺点:移动成本高
text
标记前:│A│空│B│空│C│空│
整理后:│A│B│C│空│空│空│

分代收集

text
┌───────────────────────────────────────┐
│                堆                      │
├───────────────────────────────────────┤
│  年轻代(Young Gen)                   │
│  ┌───────────────────────────────┐    │
│  │ Eden │ Survivor0 │ Survivor1 │    │
│  │ 80%  │   10%     │   10%     │    │
│  └───────────────────────────────┘    │
│  → 新对象,频繁GC,复制算法             │
├───────────────────────────────────────┤
│  老年代(Old Gen)                     │
│  → 长期存活对象,较少GC,标记-整理      │
└───────────────────────────────────────┘

GC类型

GC类型触发条件区域
Minor GCEden满年轻代
Major GC老年代满老年代
Full GC整个堆满整个堆+方法区

常见垃圾收集器

年轻代收集器

收集器算法特点
Serial复制单线程,简单高效
ParNew复制Serial多线程版
Parallel Scavenge复制关注吞吐量

老年代收集器

收集器算法特点
Serial Old标记-整理单线程
Parallel Old标记-整理关注吞吐量
CMS标记-清除低延迟,有碎片

新一代收集器

收集器区域特点
G1整堆分区收集,可预测停顿
ZGC(JDK 11+)整堆延迟<10ms
Shenandoah(JDK 12+)整堆延断停顿

CMS收集器流程

text
1. 初始标记(STW):标记GC Roots直接关联的对象
2. 并发标记:并发标记所有可达对象
3. 重新标记(STW):修正并发标记期间的变动
4. 并发清除:并发清除标记对象

G1收集器特点

text
┌─────────────────────────────────────┐
│              G1堆分区                 │
├─────────────────────────────────────┤
│ ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐│
│ │E│E│S│S│O│O│O│O│H│H│H│H│ │ │ │ ││
│ └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘│
│ E=Eden, S=Survivor, O=Old, H=Humongous│
└─────────────────────────────────────┘

特点:
- 分区收集,不连续年轻代/老年代
- 可预测停顿时间
- 无碎片(复制算法)

GC参数配置

text
# 选择收集器
-XX:+UseSerialGC        # Serial + Serial Old
-XX:+UseParNewGC        # ParNew + Serial Old
-XX:+UseParallelGC      # Parallel Scavenge + Parallel Old
-XX:+UseConcMarkSweepGC # ParNew + CMS
-XX:+UseG1GC            # G1

# 吞吐量优先
-XX:MaxGCPauseMillis=200  # 最大停顿时间
-XX:GCTimeRatio=99        # GC时间占比

# CMS参数
-XX:CMSInitiatingOccupancyFraction=70  # 老年代70%触发CMS

# G1参数
-XX:MaxGCPauseMillis=200  # 目标停顿时间
-XX:G1HeapRegionSize=4m   # Region大小

GC日志分析

text
# 开启GC日志
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:gc.log

# 示例日志
[GC (Allocation Failure) [PSYoungGen: 2048K->512K(2560K)]
 2048K->1024K(10240K), 0.0051234 secs]

注意事项

频繁Full GC是性能问题信号

CMS有内存碎片问题,必要时触发Full GC整理

G1适合大堆(>4GB),可预测停顿

合理设置堆大小减少GC频率

要点总结

  1. 垃圾判断:可达性分析,从GC Roots搜索不可达对象
  2. GC算法:标记-清除(碎片)、标记-复制(年轻代)、标记-整理(老年代)
  3. 分代收集:年轻代频繁Minor GC,老年代Major/Full GC
  4. 收集器选择:吞吐量用Parallel,延迟用CMS/G1
  5. 监控GC日志,调优内存参数

📝 发现内容有误?点击此处直接编辑

← 上一篇 Java JVM概述与跨平台原理
下一篇 → Java对象创建与内存分配
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库