# 10. JVM 垃圾回收算法有哪些?有何区别?

# 标准答案

JVM 中常见的垃圾回收算法有标记-清除算法、复制算法、标记-整理算法以及分代回收算法。每种算法有不同的应用场景和优缺点。

  • 标记-清除算法简单,但存在内存碎片问题;复制算法适用于年轻代,避免碎片化但有空间浪费;
  • 标记-整理算法解决了碎片问题,但会导致对象的移动;
  • 分代回收算法将堆分为多个区域,分别使用不同的回收算法,以优化垃圾回收的效率和性能。

# 答案解析

JVM的垃圾回收机制采用了多种回收算法,并根据不同的内存区域和对象的生命周期选择合适的算法。每种垃圾回收算法有其适用场景、优缺点,并且通过算法的不同组合来优化整个垃圾回收的效率和性能。

# 1. 标记-清除算法(Mark and Sweep)

标记-清除算法是最基础的垃圾回收算法,其工作流程分为两步:

  1. 标记阶段:从GC Root对象开始,遍历整个对象图,标记所有可达的对象。
  2. 清除阶段:清除所有未标记的对象,将其占用的内存空间释放。

优点

  • 算法简单,容易实现。
  • 不需要移动对象,因此不需要考虑对象的引用更新。

缺点

  • 内存碎片:标记-清除算法无法压缩内存,回收后会留下内存碎片,导致内存的有效使用降低。
  • 性能不稳定:每次回收都需要对整个堆进行遍历和清理。

# 2. 复制算法(Copying)

复制算法将内存划分为两个相同大小的区域,每次只使用其中一个区域。当一个区域满时,回收该区域中存活的对象并将其复制到另一个区域中。此算法适用于年轻代的垃圾回收。

优点

  • 没有碎片问题:通过将存活对象复制到空闲区域,避免了内存碎片问题。
  • 回收效率高:每次只需要遍历一部分内存,减少了回收的工作量。

缺点

  • 空间浪费:由于堆内存被分为两部分,每次只用一半,这可能导致内存空间的浪费。
  • 不适用于老年代:老年代对象较多,使用复制算法会浪费过多内存空间。

# 3. 标记-整理算法(Mark-Compact)

标记-整理算法是标记-清除算法的优化版本。其工作流程如下:

  1. 标记阶段:从GC Root对象开始,标记所有可达的对象。
  2. 整理阶段:将所有存活的对象压缩到堆的一端,释放剩余的内存空间,避免碎片化。

优点

  • 解决了内存碎片问题:通过整理阶段,回收后的内存块紧凑,避免了碎片问题。

缺点

  • 性能消耗大:由于需要移动对象,整理阶段的性能消耗较高,尤其是涉及到大量对象时,可能导致停顿时间较长。

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

分代回收算法将堆内存划分为年轻代(Young Generation)老年代(Old Generation)永久代(Permanent Generation,JDK 8之前)/元空间(Metaspace,JDK 8及之后)三个区域。该算法基于对象的生命周期进行优化,假设大多数对象生命周期较短,而少数对象会存活较长时间。

年轻代回收

  • 使用复制算法,主要回收短生命周期的对象,效率较高。

老年代回收

  • 使用标记-整理算法标记-清除算法,因为老年代中的对象生命周期较长,存活的对象比较少。

优点

  • 高效的回收策略:通过分代收集,提高了内存的利用率和回收效率。
  • 针对性回收:年轻代和老年代分别采用不同的回收策略,避免了空间浪费和性能瓶颈。

缺点

  • 不适用于长时间存活的对象:在对象存活时间较长的情况下,老年代的垃圾回收可能会导致较长的停顿时间。

# 深入追问

  1. 为什么年轻代回收使用复制算法,老年代使用标记-整理算法?
  2. 垃圾回收时,为什么有时选择标记-整理而不是标记-清除?
  3. 分代回收如何根据对象的年龄分配不同的内存区域?
  4. 分代回收的优化策略有哪些?如何减少Full GC的频率?

# 相关面试题

  • 什么是Java内存模型,如何与垃圾回收相关联?
  • JVM的不同垃圾回收器有哪些?如何选择合适的回收器?
  • 如何通过GC日志分析和优化垃圾回收过程?
  • 垃圾回收的Stop-The-World事件如何影响应用性能?