JVM垃圾回收机制详解

目录

垃圾回收(Garbage Collection,简称 GC)是编程语言中自动管理内存的核心机制,其核心目标是识别并回收 “不再被使用的对象” 所占用的内存,避免内存泄漏,确保程序高效、安全地运行。以下从核心目标、垃圾判断标准、基本流程、经典算法、常见回收器及关键概念等方面详细总结:

一、垃圾回收的核心目标

二、“垃圾” 指不再被任何存活对象引用的对象。判断对象是否存活的核心算法有两种:

1. 引用计数法(Reference Counting)

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

三、Java 中的 四种“引用” 类型(影响存活判断)Java 通过java.lang.ref包定义了 4 种引用类型,优先级从高到低:

四、经典垃圾回收算法

五、常见垃圾回收器(具体实现)

1. Serial GC(串行回收器)

特点:单线程执行 GC,回收时暂停所有应用线程(STW,Stop-The-World)。

算法:新生代用标记 - 复制,老年代用标记 - 整理。

优点:实现简单,内存占用少,适合单 CPU、小堆场景(如嵌入式设备)。

缺点:STW 时间长(堆越大,停顿越久),不适合高并发应用。

2. Parallel GC(并行回收器)

3. CMS(Concurrent Mark Sweep,并发标记清除,停顿时间短)

4. G1(局域性)混合模式

六、三色标记


垃圾回收(Garbage Collection,简称 GC)是编程语言中自动管理内存的核心机制,其核心目标是识别并回收 “不再被使用的对象” 所占用的内存,避免内存泄漏,确保程序高效、安全地运行。以下从核心目标、垃圾判断标准、基本流程、经典算法、常见回收器及关键概念等方面详细总结:


一、垃圾回收的核心目标


自动释放无用内存:无需开发者手动调用free()或delete,自动识别 “垃圾对象” 并回收其内存。
减少内存泄漏:通过精准识别无用对象,避免长期占用内存导致的程序性能下降或崩溃。
平衡效率与安全性:在保证内存回收准确性的同时,尽可能减少对程序运行的干扰(如缩短停顿时间)。


二、“垃圾” 指不再被任何存活对象引用的对象。判断对象是否存活的核心算法有两种:

1. 引用计数法(Reference Counting)


原理:为每个对象维护一个 “引用计数器”,当对象被引用时计数器 + 1,引用失效时 - 1;当计数器为 0 时,判定为垃圾。
优点:实现简单,回收及时(计数器为 0 时立即回收)。
缺点:
无法解决循环引用问题(如 A 引用 B,B 引用 A,两者计数器均不为 0,但实际已无用)。
频繁更新计数器会增加性能开销。
 

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


核心思想:以 “根对象(GCRoots)” 为起点,遍历所有可直接或间接引用的对象(形成 “引用链”);无法被根对象触达的对象判定为垃圾。
根对象(GCRoots)类型:
1.虚拟机栈中正在使用的局部变量(如方法参数、临时变量)。
2.方法区中静态变量(static修饰)和常量(final修饰)。
3.本地方法栈中 JNI(Native 方法)引用的对象。
4.虚拟机内部的引用(如类加载器、基本数据类型对应的 Class 对象)。
优点:解决循环引用问题,是 Java、C# 等语言的核心判断方式。

三、Java 中的 四种“引用” 类型(影响存活判断)
Java 通过java.lang.ref包定义了 4 种引用类型,优先级从高到低:

强引用:默认引用(如Object obj = new Object()),只要存在,对象永远不被回收。
软引用(SoftReference):内存不足时(即将 OOM)才会被回收,适合缓存场景(如图片缓存)。
弱引用(WeakReference):只要发生 GC 就会被回收,适合临时数据(如 ThreadLocal 的 key)。
虚引用(PhantomReference):仅用于跟踪对象回收过程,必须配合引用队列使用,无实际引用意义。
 


四、经典垃圾回收算法

1.标记 - 清除算法

  • 标记:通过可达性分析标记所有存活对象。
  • 清除:回收未标记的垃圾对象,保留内存地址空闲。
  • 优点:实现简单,无需移动对象,适用于存活对象多的场景。
  • 缺点:
  • 1.产生内存碎片(回收后空闲内存分散),可能导致大对象无法找到足够的连续空间可以分配。
  • 2.清除过程效率低(需遍历全堆找垃圾)。

2.复制算法

  • 将内存划分为大小相等的两块(如 From 区和 To 区),仅使用其中一块(From 区)。
  • 标记:标记 From 区中的存活对象。
  • 复制:将存活对象复制到 To 区,按顺序紧凑排列。
  • 切换:清空 From 区,下次 GC 时 From 区和 To 区角色互换。
  • 优点:无内存碎片,分配内存时只需移动指针(类似 “指针碰撞”),效率高。
  • 缺点:
  • 1.内存利用率低(仅用一半)。
  • 2.复制成本高(若存活对象多,复制耗时)。
  • 适用场景:存活对象少的区域(如 Java 新生代的 Eden 区与 Survivor 区)。

3.标记 - 整理算法

  • 标记:标记所有存活对象。
  • 整理:将存活对象向内存一端 “紧凑排列”,消除碎片。
  • 清除:回收末端的垃圾对象(因存活对象已集中,末端空闲内存可直接释放)。
  • 优点:无内存碎片,内存利用率 100%。
  • 缺点:整理阶段需要移动对象,成本高(尤其存活对象多时)。
  • 适用场景:存活对象多的区域(如 Java 老年代)。

4.分代回收算法(Generational Collection)

  • 核心依据:对象存活周期不同(“朝生夕死” 的对象占多数,少数对象长期存活)。
  • 内存划分:将堆分为新生代(Young Generation)和老年代(Old Generation):
  • 新生代:存放新创建的对象,存活时间短(多数一次 GC 就被回收)。
  • 老年代:存放存活时间长的对象(多次 GC 后仍存活)。
  • 回收策略:
  • 新生代:采用标记 - 复制算法(因存活对象少,复制成本低)。
  • 细分为 Eden 区(80%)和两个 Survivor 区(S0、S1 各 10%),每次使用 Eden+S0,回收时复制存活对象到 S1,清空 Eden+S0,下次 S0 与 S1 互换。
  • 老年代:采用标记 - 整理或标记 - 清除算法(因存活对象多,移动成本高)。
  • 优势:按代针对性回收,减少全堆扫描,提高效率。
     

五、常见垃圾回收器(具体实现)


垃圾回收器是 GC 算法的具体实现,不同回收器侧重不同(吞吐量、延迟等),以下是 Java 中主流回收器:

1. Serial GC(串行回收器)

  • 特点:单线程执行 GC,回收时暂停所有应用线程(STW,Stop-The-World)。

  • 算法:新生代用标记 - 复制,老年代用标记 - 整理。

  • 优点:实现简单,内存占用少,适合单 CPU、小堆场景(如嵌入式设备)。

  • 缺点:STW 时间长(堆越大,停顿越久),不适合高并发应用。

2. Parallel GC(并行回收器)

  • 特点:多线程执行 GC,仍有 STW,但比 Serial GC 快(利用多核优势),侧重吞吐量(吞吐量 = 运行用户代码时间 /(运行用户代码时间 + GC 时间))。
  • 算法:新生代用标记 - 复制,老年代用标记 - 整理。
  • 优点:吞吐量高,适合后台任务、批处理等对延迟不敏感的场景。
  • 缺点:STW 时间仍较长,无法满足低延迟需求。

3. CMS(Concurrent Mark Sweep,并发标记清除,停顿时间短)

  • 特点:以低延迟为目标,尽可能减少 STW 时间,核心阶段与应用线程并发执行。
  • 流程(基于三色标记算法):
  • 初始标记(STW):标记根对象直接引用的对象(快)。
  • 并发标记:与应用线程并行,遍历根对象可达的所有对象(无 STW)。
  • 重新标记(STW):修正并发标记中因应用线程修改引用导致的漏标(快)。
  • 并发清除:与应用线程并行,回收垃圾对象(无 STW)。
  • 优点:STW 时间短,适合对延迟敏感的应用(如 Web 服务)。
  • 缺点:
  • 并发阶段占用 CPU 资源,降低程序吞吐量。
  • 产生内存碎片(基于标记 - 清除),需定期 Full GC 整理。
  • 无法处理 “浮动垃圾”(并发清除时新产生的垃圾,需下次 GC 回收)。

4. G1(局域性)混合模式

  • 特点:区域化分代式回收器,兼顾吞吐量和延迟,支持大堆(可达数 TB)。
  • 内存划分:将堆分为多个大小相等的 Region(区域),每个 Region 可动态标记为新生代(Eden/Survivor)或老年代。
  • 核心思想:优先回收 “垃圾多的 Region”(Garbage-First),减少回收时间。
  • 流程(基于三色标记 + SATB 原始快照):
  • 初始标记(STW):标记根对象直接引用。
  • 并发标记:遍历可达对象,计算每个 Region 的垃圾占比。
  • 最终标记(STW):处理并发阶段漏标对象。
  • 筛选回收(STW):选择垃圾占比高的 Region,复制存活对象到新 Region(同时整理碎片)。
  • 优点:可预测 STW 时间(通过参数设置最大停顿时间),无碎片,适合大堆、中低延迟场景。

混合模式:G1 采用局部性收集的思想,对于堆空间的划分,采用 Region 为单位的内存划分方式:
G1 垃圾回收器把堆划分成若干个大小相同的独立区域(Region)(按照 JVM 的实现,Region 的数量不会超过 2048 个):每个 Region 都会代表Eden 区,Survivor 区, Humongous(G1 用来分配大对象的区域,对于 Humongous 也分配不下的超大对象,会分配在连续的 N 个 Humongous 中),剩余的深蓝色代表的是 Old 区,灰色的代表的是空闲的 region。
这种思想上的转变和设计,使得 G1 可以面向堆内存任何部分来组成回收集来进行回收,衡量标准不再是它属于哪个分代,而是哪块内存存放的垃圾最多,回收收益最大,这就是 G1 收集器的 Mixed GC 模式,即混合 GC 模式。

垃圾收集器

过程

算法

缺点

优点

Serial(新生代)

Serial Old(老年代)

标记-复制

标记-整理

标记-复制

标记-整理

1.内存利用率低(仅用一半)。
2.复制成本高(若存活对象多,复制耗时)

实现简单,内存占用少,适合单 CPU、小堆场景(如嵌入式设备)

ParNew(新生代)

(多线程)标记-复制

标记-复制

同上

同上

Parallel Scavenge(新生代)

Parallel Old(老年代)

(多线程)标记-复制

(多线程)标记-整理

标记-复制

标记-整理

不能保证单次的停顿时间

只关注吞吐量(可以手动调整参数),适用于后台任务

CMS(老年代)

1. 初始标记,用极短的时间标记出 GC Roots 能直接关联到的对象。

2. 并发标记,标记所有的对象,用户线程不需要暂停。

3. 重新标记,由于并发标记阶段有些对象会发生了变化,存在错标、漏标等情况,需要重新标记。

4. 并发清理,清理死亡的对象,用户线程不需要暂停。

标记-清除

1.内存碎片(清除操作不会移动对象来填补空间)

2.在并发清除阶段,用户线程还没有停止,还会产生新的垃圾,这就是“浮动垃圾”

3.在并发标记和并发清除时会和用户线程抢占资源

1.STW时间最短

2.多线程

G1(局域性收集)

  1. 初始标记(STW):标记根对象直接引用。
  2. 并发标记:遍历可达对象,计算每个 Region 的垃圾占比。
  3. 最终标记(STW):处理漏标对象。
  4. 筛选回收(STW):选择垃圾占比高的 Region,复制存活对象到新 Region(同时整理碎片)。

标记,整理加复制

1.不适合小场景下使用

1.可以控制垃圾的回收的停顿时间

2.不会产生内存碎片

六、三色标记

白色:1.默认 2.垃圾 3.未被标记

灰色:自身已被标记到,但引用的子对象还未被标记

黑色:自身以及所引用的所有子对象都已被标记

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值