-
MarkSweep 标记清除算法:这个算法分为两个阶段,1.标记阶段:把垃圾内存标记出来,2.清除阶段:直接将垃圾内存回收。这种算法是⽐较简单的,但是有个很严重的问题,就是会产⽣⼤量的内存碎⽚。
-
Copying 拷⻉算法:为了解决标记清除算法的内存碎⽚问题,就产⽣了拷⻉算法。拷⻉算法将内存分为⼤⼩相等的两半,每次只使⽤其中⼀半。垃圾回收时,将当前这⼀块的存活对象全部拷⻉到另⼀半,然后当前这⼀半内存就可以直接清除。这种算法没有内存碎⽚,但是他的问题就在于浪费空间。⽽且,他的效率跟存货对象的个数有关。
-
MarkCompack 标记压缩算法:为了解决拷⻉算法的缺陷,就提出了标记压缩算法。这种算法在标记阶段跟标记清除算法是⼀样的,但是在完成标记之后,不是直接清理垃圾内存,⽽是将存活对象 往⼀端移动,然后将端边界以外的所有内存直接清除。
这三种算法各有利弊,各⾃有各⾃的适合场景。
在 JVM 中,垃圾回收算法是垃圾回收器(Garbage Collector, GC)的核心组成部分,用于识别和回收不再使用的对象。JVM 提供了多种垃圾回收算法,每种算法适用于不同的场景和需求。下面我们详细介绍 JVM 中的主要垃圾回收算法及其特点。
1. 标记-清除算法(Mark-Sweep)
(1)原理
-
标记:从 GC Roots 出发,遍历所有可达对象,并标记它们为活动对象。
-
清除:遍历堆内存,释放所有未被标记的对象占用的内存。
(2)优点
-
实现简单,适用于大多数场景。
(3)缺点
-
会产生内存碎片,影响内存分配效率。
-
清除阶段需要遍历整个堆内存,性能较低。
(4)适用场景
-
适用于老年代垃圾回收。
2. 标记-整理算法(Mark-Compact)
(1)原理
-
标记:从 GC Roots 出发,遍历所有可达对象,并标记它们为活动对象。
-
整理:将所有活动对象移动到内存的一端,减少内存碎片。
(2)优点
-
避免了内存碎片,提高了内存分配效率。
(3)缺点
-
整理阶段需要移动对象,增加了额外的开销。
(4)适用场景
-
适用于老年代垃圾回收。
3. 复制算法(Copying)
(1)原理
-
将堆内存分为两个区域:From 区和 To 区。
-
复制:将 From 区中的活动对象复制到 To 区,并清空 From 区。
-
交换:交换 From 区和 To 区的角色。
(2)优点
-
避免了内存碎片,提高了内存分配效率。
-
复制算法的效率较高,适合处理存活对象较少的情况。
(3)缺点
-
需要额外的内存空间(From 区和 To 区)。
-
适合存活对象较少的情况,如果存活对象较多,复制开销较大。
(4)适用场景
-
适用于新生代垃圾回收。
4. 分代收集算法(Generational Collection)
(1)原理
-
将堆内存分为新生代(Young Generation)和老年代(Old Generation)。
-
新生代:使用复制算法(Copying)。
-
老年代:使用标记-清除算法(Mark-Sweep)或标记-整理算法(Mark-Compact)。
(2)优点
-
根据对象的生命周期采用不同的回收策略,提高了垃圾回收的效率。
(3)缺点
-
实现复杂,需要维护多个内存区域。
(4)适用场景
-
适用于大多数 JVM 垃圾回收器。
5. 增量收集算法(Incremental Collection)
(1)原理
-
将垃圾回收过程分为多个小步骤,与应用线程交替执行。
-
每次只回收一部分垃圾,减少单次回收的暂停时间。
(2)优点
-
减少了单次回收的暂停时间,提高了应用的响应速度。
(3)缺点
-
总体回收时间可能较长。
-
实现复杂,需要额外的同步机制。
(4)适用场景
-
适用于需要低延迟的应用。
6. 并发标记清除算法(Concurrent Mark-Sweep, CMS)
(1)原理
-
初始标记:标记 GC Roots 直接引用的对象(需要暂停应用线程)。
-
并发标记:并发标记所有可达对象。
-
重新标记:修正并发标记期间发生变化的对象(需要暂停应用线程)。
-
并发清除:并发清除所有未被标记的对象。
(2)优点
-
减少了暂停时间,提高了应用的响应速度。
(3)缺点
-
会产生内存碎片。
-
需要额外的 CPU 资源。
(4)适用场景
-
适用于需要低延迟的应用。
7. G1 算法(Garbage-First)
(1)原理
-
将堆内存划分为多个区域(Region),每个区域可以是新生代或老年代。
-
初始标记:标记 GC Roots 直接引用的对象(需要暂停应用线程)。
-
并发标记:并发标记所有可达对象。
-
最终标记:修正并发标记期间发生变化的对象(需要暂停应用线程)。
-
筛选回收:优先回收垃圾最多的区域(Garbage-First)。
(2)优点
-
平衡了吞吐量和延迟。
-
避免了内存碎片。
(3)缺点
-
实现复杂,需要维护多个区域。
(4)适用场景
-
适用于大内存和多核 CPU 的应用。
8. 总结
算法 | 原理 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
标记-清除 | 标记活动对象,清除未标记对象。 | 实现简单。 | 产生内存碎片,性能较低。 | 老年代垃圾回收。 |
标记-整理 | 标记活动对象,整理内存。 | 避免内存碎片。 | 整理阶段开销较大。 | 老年代垃圾回收。 |
复制算法 | 将活动对象复制到另一个区域。 | 避免内存碎片,效率高。 | 需要额外内存空间。 | 新生代垃圾回收。 |
分代收集 | 根据对象生命周期采用不同策略。 | 提高回收效率。 | 实现复杂。 | 大多数 JVM 垃圾回收器。 |
增量收集 | 将回收过程分为多个小步骤。 | 减少单次回收的暂停时间。 | 总体回收时间较长。 | 需要低延迟的应用。 |
并发标记清除 | 并发标记和清除。 | 减少暂停时间。 | 产生内存碎片,需要额外 CPU 资源。 | 需要低延迟的应用。 |
G1 算法 | 分区回收,优先回收垃圾最多的区域。 | 平衡吞吐量和延迟,避免内存碎片。 | 实现复杂。 | 大内存和多核 CPU 的应用。 |
9. 一句话总结
JVM 提供了多种垃圾回收算法,包括标记-清除、标记-整理、复制算法、分代收集、增量收集、并发标记清除和 G1 算法,每种算法适用于不同的场景和需求。