Bootstrap

高级java每日一道面试题-2024年10月24日-JVM篇-说一下JVM有哪些垃圾回收器?

如果有遗漏,评论区告诉我进行补充

面试官: 说一下JVM有哪些垃圾回收器?

我回答:

1. Serial收集器

  • 特点:Serial收集器是最古老、最稳定的收集器,它使用单个线程进行垃圾收集工作。在进行垃圾回收时,它会暂停所有用户线程,即Stop The World(STW)。
    • 单线程工作,适合单核 CPU。
    • 在年轻代(Young Generation)中使用,称为 Serial。
    • 在老年代(Old Generation)中使用,称为 Serial Old。
    • 收集速度快,但会暂停整个应用。
    • 适合小型应用或测试环境。
  • 应用场景:适用于单CPU环境或内存较小、对停顿时间要求不高的应用。
  • 回收算法
    • 在新生代,它采用复制算法,将新生代划分为一个 Eden 区和两个 Survivor 区,默认比例是 8:1:1。在垃圾回收时,将 Eden 区和其中一个 Survivor 区中存活的对象复制到另一个 Survivor 区。
    • 在老年代,它采用标记 - 整理算法,对存活对象进行标记后,将其移动到一端,清除另一端的垃圾。

2. ParNew收集器

  • 特点:ParNew收集器是Serial收集器的多线程版本,也被称为“并行年轻代收集器”。它可以与CMS收集器配合使用,以实现更短的停顿时间。
    • 多线程工作,适合多核 CPU。
    • 在年轻代中使用,称为 ParNew 或者 Parallel。
    • 专注于吞吐量优化。
    • 使用复制算法(Copy)或标记-压缩算法(Mark-Compact)。
    • 适合需要高吞吐量的应用。
  • 应用场景:适用于多CPU环境,特别是需要缩短年轻代垃圾回收停顿时间的应用。
  • 回收算法
    • 在新生代采用复制算法,其内存区域划分和 Serial 回收器相同,也是 Eden 区和两个 Survivor 区。

3. Parallel收集器(Parallel Scavenge)

  • 特点:Parallel收集器是一种多线程并行的垃圾收集器,它关注点是吞吐量(运行用户代码时间与CPU总消耗时间的比值)。它同样适用于新生代和老年代的回收。
  • 应用场景:适用于对吞吐量有较高要求的应用,如后台计算任务等。
  • 回收算法
    • 在新生代采用复制算法,通过参数调整可以控制新生代的大小、Survivor 区比例等内存分配策略,以优化吞吐量。

4. CMS(Concurrent Mark-Sweep)收集器

  • 特点:CMS收集器是一种以获取最短回收停顿时间为目标的收集器。它是第一款并发收集器,可以实现垃圾收集线程与用户线程同时工作。但需要注意的是,CMS收集器对CPU资源敏感,且无法处理浮动垃圾,还可能产生大量空间碎片。
    • 并发标记和清扫,减少暂停时间。
    • 专注于降低停顿时间。
    • 使用标记-清除算法(Mark-Sweep)。
    • 可能会产生碎片。
    • 适合对响应时间敏感的应用。
  • 应用场景:适用于对停顿时间有严格要求的应用,如Web服务等。

5. G1(Garbage-First)收集器

  • 特点:G1收集器是一种面向服务端应用的垃圾收集器,它逐步取代了CMS收集器。G1收集器具有更可控的停顿时间和高效的并发能力。它将堆内存划分为多个大小相等的独立区域(Region),并根据对象的存活周期和垃圾回收的优先级来选择要回收的区域。
    • 设计目标是在控制 GC 停顿时间的同时获得高吞吐量。
    • 使用分区(Region)的概念,将堆划分为多个小块。
    • 并发执行,适合大堆(Large Heap)。
    • 自适应调整策略,自动选择最优的收集时机。
    • 适合对停顿时间和吞吐量都有较高要求的应用。
  • 应用场景:适用于对停顿时间和吞吐量都有较高要求的大型应用。
  • 回收过程及算法
    • 初始标记(Initial Mark):需要暂停所有用户线程,标记从 GC Roots 能直接关联到的对象,时间很短。
    • 并发标记(Concurrent Mark):与用户线程并发执行,对堆中对象进行可达性分析,标记存活对象。
    • 最终标记(Final Mark):需要暂停所有用户线程,处理并发标记阶段遗留的少量的 SATB(Snapshot - At - The - Beginning)记录。
    • 筛选回收(Live Data Counting and Evacuation):对各个区域的回收价值和成本进行排序,根据设定的停顿时间,选择部分区域进行回收,回收过程中采用复制算法,将存活对象复制到新的区域。

6. ZGC Collector(Z Garbage Collector)

  • 特点
    • 专注于极低的 GC 停顿时间。
    • 使用并行与并发技术。
    • 适合非常大的堆(数 TB)。
    • 适用于需要极高响应时间的应用。
    • 从 Java 11 开始引入。

7. Shenandoah Collector

  • 特点
    • 类似于 ZGC,专注于极低的 GC 停顿时间。
    • 使用并行与并发技术。
    • 适合需要极高响应时间的应用。
    • 从 Java 9 开始作为实验性功能,Java 12 作为正式功能引入。

8. Parallel Old Collector

  • 特点
    • 与 Parallel Collector 配合使用。
    • 在老年代中使用,称为 Parallel Old。
    • 适用于需要高吞吐量的应用。
  • 回收算法
    • 采用标记 - 整理算法进行老年代垃圾回收。

9. Serial Old Collector

  • 特点

    • 与 Serial Collector 配合使用。
    • 在老年代中使用,称为 Serial Old。
    • 适用于小型应用或测试环境。
  • 适用场景

    • 主要用于 Client 模式下的老年代垃圾回收,或者在 Server 模式下与 Parallel Scavenge 回收器搭配使用,用于处理 CMS 回收器产生的垃圾碎片等情况。
  • 回收算法

    • 采用标记 - 整理算法进行老年代垃圾回收。

10. Concurrent Mark Sweep Collector (CMS)

  • 特点
    • 已经在 Java 9 中被标记为废弃,并在 Java 14 中被移除。
    • 曾经用于降低停顿时间,但在现代 JVM 中已经被 G1 等更先进的收集器取代。
  • 回收过程及算法
    • 初始标记(Initial Mark):需要暂停所有用户线程,标记从 GC Roots 能直接关联到的对象,速度很快。
    • 并发标记(Concurrent Mark):可以与用户线程并发执行,从初始标记的对象开始,遍历整个老年代,标记所有存活的对象。
    • 重新标记(Remark):需要暂停所有用户线程,修正并发标记期间因用户线程继续运行而导致标记变动的那部分对象的标记记录,这个阶段的停顿时间比初始标记长,但远比并发标记短。
    • 并发清除(Concurrent Sweep):与用户线程并发执行,清除未被标记的垃圾对象。
    • 整体采用标记 - 清除算法,在并发清除阶段会产生一定的内存碎片。

垃圾回收算法简介

为了更好地理解垃圾回收器的工作原理,以下是几种常见的垃圾回收算法的简要介绍:

  • 标记-清除算法:该算法分为标记和清除两个阶段。在标记阶段,垃圾回收器会从GC Roots开始遍历所有可达的对象,并标记它们为活动对象。在清除阶段,垃圾回收器会遍历整个堆,回收所有未被标记的对象的内存。但这种方法可能会产生大量不连续的内存碎片。
  • 标记-整理算法:该算法是标记-清除算法的改进版本。它在标记和清除的基础上增加了整理阶段,将所有活动对象向一端移动,从而消除内存碎片。但这种方法可能会增加额外的开销。
  • 复制算法:该算法通常用于新生代垃圾回收。它将内存分为Eden空间和两个Survivor空间。在垃圾回收时,将还存活的对象复制到另一个Survivor空间或老年代中,然后清空当前使用的空间。这种方法可以减少内存碎片的产生,但可能会浪费一定的空间。

总结

选择合适的垃圾回收器需要根据应用程序的具体需求和运行环境进行权衡。以下是一些选择垃圾回收器时需要考虑的因素:

  • 内存大小:对于大内存应用,G1和CMS可能是更好的选择。
  • 吞吐量:如果对吞吐量有较高要求,Parallel GC是较好的选择。
  • 停顿时间:对于对响应时间要求较高的应用,CMS和G1可能更合适。
  • CPU资源:并行和并发垃圾收集器会占用额外的CPU资源来进行垃圾回收工作。如果应用程序本身已经对CPU资源有较高的需求,那么需要谨慎选择垃圾收集器,以避免对应用程序性能造成过大影响。
  • 垃圾产生速度:如果应用程序产生垃圾的速度非常快,那么需要选择能够高效处理大量垃圾的收集器,如G1或Parallel GC。
  • JVM版本:不同版本的JVM可能支持不同的垃圾收集器,且同一收集器在不同版本中的表现也可能有所不同。因此,在选择垃圾收集器时,还需要考虑JVM的版本兼容性。

垃圾收集器的调优是一个复杂的过程,需要根据具体的应用场景和性能目标进行调整。以下是一些常见的调优策略:

  • 设置合适的堆内存大小:堆内存设置过大或过小都会影响垃圾收集器的性能。过大可能导致垃圾回收时间过长,过小则可能频繁触发垃圾回收。因此,需要根据应用程序的内存需求来设置合适的堆内存大小。

JVM的垃圾回收器有多种类型,每种类型都有其特定的应用场景和优势。在选择垃圾回收器时,需要根据应用的特点和需求进行权衡和选择。同时,了解垃圾回收算法的基本原理也有助于更好地理解垃圾回收器的工作原理和优化策略。

;