Java基础知识总结:Java 常见的垃圾收集器总结

版权声明:本文遵守创作共享 CC BY-NC-SA 3.0协议 https://blog.csdn.net/chen_kkw/article/details/86600271

从 Java 到 Golang,垃圾收集器几乎成为现代语言的标配,让编码人员可以从手动释放内存的繁琐中彻底解放出来,提高了编码效率,那么在 Java 中你知道有哪些常见的垃圾收集器么,它们经历了怎样的演进呢?

实际上,垃圾收集器并非是统一的,它和具体的 JVM 实现相关,本文主要谈谈最主流 的 Oracle JDK。从年代上来说,收集器可以按照其工作的不同年代区间分为新生代收集器和老年代收集器,再加上近期推出的 G1 和 ZGC 这两款不再严格按照条带装年龄层来工作的收集器,属于最前沿的产品。

新生代收集器

Serial GC

最早的实现,单线程,会出现“stop-the-world”现象,因为它在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集完成。目前是 HotSpot 运行在 Client 模式下的默认新生代收集器,它优点在于:简单而高效,对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得更高的单线程收集效率。

ParNew GC

是个新生代GC实现,它实际是Serial GC的多线程版本,最常见的应用场景是配合老年代的CMS GC工作。CMS收集器是JDK 1.5推出的一个具有划时代意义的收集器

Parallel Scavenge

Parallel Scavenge收集器也是一个并行的多线程新生代收集器,它也使用复制算法。Parallel Scavenge收集器的特点是它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间,而 Parallel Scavenge 收集器的目标是达到一个可控制的吞吐量。

老年代收集器

Serial Old

Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记整理算法。这个收集器的主要意义也是在于给Client模式下的虚拟机使用。

Parallel Old

Parallel Old 是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。这个收集器在1.6中才开始提供。

在此之前,如果新生代选择了Parallel Scavenge收集器,老年代除了Serial Old以外别无选择,所以在Parallel Old诞生以后,“吞吐量优先”收集器终于有了比较名副其实的应用组合,在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器。

CMS(Concurrent Mark Sweep) GC

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。基于标记-清除(Mark-Sweep)算法,设计目标是尽量减少停顿时间,这一点对于Web应用等对延迟时间敏感的程序来说至关重要。

CMS收集器工作的整个流程分为以下4个步骤:

  1. 初始标记(CMS initial mark):因为仅仅只需要标记一下GC Roots 能直接关联到的对象,所以速度很快,但也是需要 “Stop The World”。
  2. 并发标记(CMS concurrent mark):进行 GC Roots Tracing 的过程,在整个过程中耗时最长。
  3. 重新标记(CMS remark):为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。此阶段也需要 “Stop The World”。
  4. 并发清除(CMS concurrent sweep)

由于整个过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作。所以,从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。

一直到今天,仍然有很多系统使用 CMS GC 。但是它也有问题, CMS 采用的标记-清除算法,存在着内存碎片化问题,所以难以避免在长时间运行等情况下发生 Full GC ,导致无法忍受的停顿。另外,既然强调了并发( Concurrent ),CMS 会占用更多 CPU 资源,并和用户线程争抢。

G1 GC

这是一种兼顾吞吐量和停顿时间的GC实现,是Oracle JDK 9以后的默认GC选项。G1可以直观的设定停顿时间的目标,相比于CMS GC,G1未必能做到CMS在最好情况 下的延时停顿,但是最差情况要好很多。

G1 GC 仍然存在着年代的概念,但是其内存结构并不是简单的条带式划分,而是类似棋盘的一个个 region 。 Region 之间是复制算法,但整体上实际可看作是标记 - 整理( MarkCompact )算法,可以有效地避免内存碎片,尤其是当 Java 堆非常大的时候, G1 的优势更加明显。 G1 吞吐量和停顿表现都非常不错,并且仍然在不断地完善,与此同时 CMS 已经在 JDK 9 中被标记为废弃( deprecated )。

ZGC

ZGC 是 Java 11 新加入的,号称可以达到10ms 以下的 GC 停顿,目前对应的中文资料较少,google 出来的文章也较少,可以肯定是个值得期待的新技术。

没有更多推荐了,返回首页