【Java】垃圾回收机制

Java 的垃圾回收机制(Garbage Collection,GC)是 JVM 自动管理内存的重要功能,目的是回收程序运行过程中不再使用的对象,避免内存泄漏和溢出。


一、为什么需要垃圾回收?

  • Java 使用 自动内存管理(不像 C++ 要手动释放内存)
  • 程序中可能存在大量临时对象,一旦无引用就可以被 GC 回收
  • 避免手动释放内存带来的错误(悬挂指针、重复释放等)

二、哪些内存区域会被 GC?

内存区域 是否 GC 管理 说明
Java 堆(Heap) ✅ 是 主要 GC 管理区域,所有对象都分配在堆上
方法区(元空间) ✅(部分回收) 主要回收废弃类信息、常量池(如无引用)
虚拟机栈 ❌ 否 局部变量表由线程控制,不由 GC 管理

三、对象如何判定“可回收”?

1. 引用计数法(Java 没用)

通过对象被引用的次数判断是否可回收,缺点:循环引用无法回收。

2. 可达性分析法(Reachability Analysis)

从一组被称为 GC Roots 的对象开始向下搜索,能到达的对象是“存活”的,不能到达的就是“可回收”的。

GC Roots 包括:

  • 栈中引用的对象(如局部变量)
  • 方法区中的静态字段引用的对象
  • JNI 引用的对象(native 代码)

四、垃圾收集算法

算法 适用区域 特点
标记-清除(Mark-Sweep) 老年代 标记可达对象,清除不可达对象,碎片多
复制算法(Copying) 新生代 把存活对象复制到另一区域,速度快,空间换时间
标记-压缩(Mark-Compact) 老年代 标记后整理存活对象,避免碎片
分代收集(Generational GC) 整个堆 按对象生命周期分代:新生代频繁回收,老年代少回收

  1. 新生代回收
    • 一般只有少量对象存活,大量死亡
    • 复制存活对象 + 整块清空 Eden 效率高,避免碎片
  2. 老年代回收
    • 对象大、存活率高,不适合频繁复制(太慢)
    • 先标记所有存活对象,再整理到一块,避免碎片
    • 比复制算法省空间、比标记-清除更干净

五、内存分代模型(JVM 默认)

               Java 堆(Heap)
 ┌────────────────────────────────────┐
 │               老年代                │← 老对象,生命周期长
 │                                    │
 ├───────────────┬────────────────────┤
 │  Eden区       │   Survivor区(S0/S1) │← 新生代,对象新建后优先放入 Eden
 │               │                    │
 └───────────────┴────────────────────┘
  • 新生代(Young):Eden + Survivor S0 + Survivor S1
  • 老年代(Old):大对象、长生命周期对象
  • 永久代 / 元空间(Method Area):类结构、常量池、静态变量等

六、常见垃圾回收器

回收器名 代别 特点
Serial 新生代、老年代 单线程、简单、适用于小内存
ParNew 新生代 多线程版本的 Serial
Parallel 新生代、老年代 高吞吐量、适用于后台任务
CMS 老年代 低停顿,标记-清除,适合响应快的应用
G1 新生代+老年代 分区收集、低停顿、适合大堆应用
ZGCShenandoah 新生代+老年代 超低停顿(JDK11+),响应实时性高

在 Java 中,垃圾回收器(Garbage Collector, GC) 是 JVM 的核心组件之一。它根据不同的回收策略和算法,自动释放堆内存中不再使用的对象。JVM 提供了多种垃圾回收器,适用于不同类型的应用和性能需求。


常见垃圾回收器分类(按代)

代别 垃圾回收器 特点
新生代 Serial、ParNew、Parallel Scavenge 处理频繁的短命对象
老年代 Serial Old、CMS、Parallel Old 回收长期存活对象
新生代 + 老年代 G1、ZGC、Shenandoah 支持全堆回收,低延迟

主要垃圾回收器详解

  1. Serial(单线程)
  • 新生代回收器,使用 复制算法
  • 仅用 一个线程 进行垃圾回收
  • Stop-The-World(暂停所有应用线程)
  • 适用于小堆、单核环境(如客户端)
  1. ParNew
  • 是 Serial 的多线程版本,并行收集
  • 新生代回收器,仍使用复制算法
  • 常与 CMS 搭配使用
  • 在多核服务器上性能更好
  1. Parallel Scavenge(吞吐量优先)
  • 又叫 吞吐量回收器
  • 新生代使用复制算法,多线程
  • 可搭配 Parallel Old 形成“吞吐量优先”的组合
  • 适合批处理、大数据、后台任务系统
  1. CMS(Concurrent Mark Sweep)
  • 老年代回收器,低停顿,标记-清除算法
  • 并发增量方式进行 GC,减少 STW 时间
  • 缺点:会产生内存碎片,回收不彻底,需 Full GC 辅助
  1. G1(Garbage First)
  • Java 9 默认回收器(Java 11+ 更稳定)
  • 把堆划分成多个 Region,按区域回收
  • 并发回收、分代混合、低延迟
  • 适合大堆(4GB+)的服务端应用
  1. ZGC(超低延迟 GC)
  • Java 11+ 支持,适合 低延迟应用(RT<10ms)
  • 可管理数 TB 的堆,最大暂停时间<10ms
  • 基于 Region + 并发标记/重定位
  • 使用 读屏障(Read Barrier)
  1. Shenandoah(低停顿 GC)
  • OpenJDK 中的另一款 低延迟收集器
  • 和 ZGC 类似,回收过程大部分并发
  • 停顿时间与堆大小无关,适合需要实时响应的系统

不同 GC 回收器对比

回收器 回收代 算法 是否并发 吞吐量 停顿时间 特点
Serial 新、老 复制、整理 单线程,适合小程序
ParNew 新生代 复制 多线程,配合 CMS
Parallel 新、老 复制、整理 ✅ 高 高吞吐量,适合批处理
CMS 老年代 标记-清除 ✅ 是 ✅ 低 并发、低延迟、易碎片
G1 全堆 区域化混合 ✅ 是 ✅ 低 分区回收,控制延迟
ZGC 全堆 并发重定位 ✅ 是 ✅ 极低 支持超大堆,延迟<10ms
Shenandoah 全堆 并发整理 ✅ 是 ✅ 极低 与堆大小无关的延迟控制

如何选择垃圾回收器?

  • 小堆、单线程 → Serial
  • 多核、服务端 → ParNew + CMSParallel
  • 对吞吐量敏感(如离线处理)→ Parallel + ParallelOld
  • 对响应时间敏感(如Web服务器)→ CMSG1
  • 对延迟极敏感(金融、游戏)→ G1ZGCShenandoah

设置垃圾回收器参数(JVM)

# 使用 G1
-XX:+UseG1GC

# 使用 CMS
-XX:+UseConcMarkSweepGC

# 使用 Parallel
-XX:+UseParallelGC

# 使用 ZGC(JDK11+)
-XX:+UseZGC

七、GC 触发条件(以 G1 为例)

  • Eden 区满
  • Old 区占用超过阈值(如 45%)
  • 显式调用 System.gc()(不推荐)
  • Full GC:老年代或方法区不足、元空间类卸载等

八、GC 日志和调优常用参数

-XX:+PrintGCDetails        # 打印 GC 日志
-XX:+UseG1GC               # 使用 G1 收集器
-Xms / -Xmx                # 设置初始/最大堆大小
-XX:NewRatio               # 新生代与老年代的比例
-XX:SurvivorRatio          # Eden 与 Survivor 的比例

总结

方面 内容
判断方式 可达性分析(GC Roots)
回收区域 Java 堆(新生代 + 老年代)、方法区(部分)
常用算法 复制、标记-清除、标记-整理
分代策略 不同对象生命周期使用不同策略提升效率
收集器 Serial、CMS、G1、ZGC 等,适配不同场景

你可能感兴趣的:(Java,java,jvm,开发语言)