垃圾收集器学习笔记

GC算法(计数/复制/标记清除/标记整理)是内存回收的方法论,垃圾收集器是算法的具体落地实现。垃圾回收发生在堆内存和方法区。

值得注意的是,目前为止还没一完美的垃圾收集器,更加没有万能的收集器,只能针对具体的应用最合适的收集器,进行分代收集。

本篇笔记记录了四种类型的垃圾收集器。

1、Serial:串行垃圾回收器

它为单线程环境设计并且只使用一个线程进行垃圾回收,会暂停所有的用户进程,所以不适合服务器环境。

想象一个场景:你正在和朋友在餐厅吃饭,一个服务员跑过来跟你说:你们先停一下唉,我擦下桌子,擦好你们再吃。串行垃圾回收就类似这么个理念。你想一下,双十一,你正在下单付款,结果服务器来了一下串行GC,还让不让人活了?

串行收集器是最古老的、最稳定的垃圾收集器,只使用一个线程去回收。但在垃圾收集过程中可能会产生较长时间的停顿(Stop-The-World状态)。虽然在收集垃圾过程中需要暂停其所有其他的工作线程,但是它简单高效,对于限定单个CPU环境来说,没有线程交互的开销可以获得最高的垃圾回收效率,因此Serial垃圾收集器依然是java虚拟机运行在Client模式下的默认的新生代垃圾收集器。

如图所示:

对应的JVM参数是:-XX:+UseSerialGC,开启之后将会使用:Serial(young区使用) + Serial Old(Old区使用)的收集器组合。

它表示新生代老年代都会使用串行垃圾回收器,其中新生代使用复制算法,老年代使用标记整理算法。

关于它的特点,再啰嗦一句:它是一个单线程的垃圾收集器,当进行垃圾回收的时候,其它线程必须暂停工作直至它收集结束。

2、Parallel:并行垃圾回收器

多个垃圾回收线程并行工作,此时用户线程是暂停的,适用于科学计算、大数据处理等交互比较弱的场景。

这个和上边的差不多,原来是来了一个服务员擦桌子,这个是来了一群服务员擦桌子。

注意这里的并行GC是发生在新生代的,至于老年代是不是用并行的方式,取决于具体使用的哪一种并行垃圾回收器。

3、CMS:并发垃圾回收器

CMS:Current Mark Sweep,并发标记清除,是一种以获取最短回收停顿时间为目标的收集器。用户线程和垃圾收集线程同时执行(不一定是并行的,可能是交替执行),不需要停顿用户进程,互联网公司多使用它,适用于堆响应时间有要求的场景。

这个场景类似于,来了一个服务员给你说:你们先到旁边桌子吃,我擦好桌子你们再回来。

这个收集器用于老年代,多个线程并发回收垃圾,使用的是标记清除算法。

整个回收垃圾的过程分为如下 4 步:

1)、初始标记(CMS initial mark):只是仅仅标记GC Root 能够直接关联的对象,速度很快,但是需要“Stop The World”  

2)、并发标记(CMS concurrent mark):进行GC Root Tracing的过程,简单来说就是遍历Initial Marking阶段标记出来的存活对象,然后继续递归标记这些对象可达的对象。

3)、重新标记(CMS Remark):修正并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,需要“Stop The World”。这个时间一般比初始标记长,但是远比并发标记时间短。

4)、并发清除(CMS concurrent sweep):对上一步标记的对象进行清除操作。

由于整个过程最耗时的操作是第二(并发标记)、四步(并发清除),而这两步垃圾收集器线程是可以和用户线程一起工作的。所以整体来说,CMS垃圾收集和用户线程是一起并发的执行的。

CMS缺点:

1)、对CPU资源敏感

因为在并发阶段,会占用一部分CPU资源,从而导致应用程序变慢,总吞吐量会降低。

2)、产生浮动垃圾

由于CMS并发清理阶段用户线程还在工作,这个时候产生的垃圾,CMS无法在本次收集中处理掉它们,只能留在下一次GC时再将其处理掉,这部分垃圾称为“浮动垃圾”。

3)、产生内存垃圾碎片

因为采用的算法是标记-清除,很明显,会有空间碎片产生。

4、G1垃圾回收器

CMS垃圾收集器虽然减少了暂停应用程序的运行时间,但是它还存在着内存碎片的问题。于是,为了去除内存碎片,同时保留CMS垃圾收集器暂停时间短的优点,java7发布了一款新的垃圾收集器-G1.

G1收集器技术发展的最前沿的成果,可以实现在基本不牺牲吞吐量的前提下完成低停顿的内存回收。首发于JDK8中,是JDK9默认的垃圾回收器。

主要的改变是Eden、Survivor和Tenured等内存区域不再是连续的了,而是变成了一个个大小一样的Region。每个Region大小从1MB到32MB不等。一个Region可能属于Eden、Survivor或者Tenured内存区域。

官方对它的描述是,G1是一种服务器端应用的垃圾收集器,应用于多处理器和大容量内存环境中,在实现高吞吐量的同时,尽可能满足垃圾收集暂停时间的要求。此外,它还具有以下的特性:

  • 像CMS收集器一样,能与应用程序线程并发执行;
  • 整空闲空间更加快速;
  • 不希望牺牲大量的 吞吐性能;
  • 不需要更大的Java Heap

G1收集器的设计目标是取代CMS收集器,它与CMS相比,在以下方面的表现更加出色:

  • G1是一个有整理内存过程的垃圾收集器,不会产生很多的内存碎片;
  • G1的stop the world更加可控,G1在停顿时间上添加了预测机制,用户可以指定停顿时间。

我们先来看一下其它的垃圾收集器都各有什么特点:

  • 年轻代和老年代是各自独立且连续的内存块;
  • 年轻代收集使用单Eden+S0+S1复制算法;
  • 老年代收集必须扫描整个老年代区域;
  • 都是以尽可能少而快速的执行GC为设计原则。

它并不像前面介绍的所有垃圾收集器是区分新生代,老年代的,它作用于全区域。将整个Java堆划分为多个大小固定的独立区域(Regin),并且跟踪这些区域的垃圾堆积面积,在后台维护一个优先级列表,每次根据允许的收集时间,优先回收垃圾最多的区域,这样保证了G1收集器在有限的时间内可以获得最高的收集效率。



posted @ 2020-01-21 21:19  Simon-Lau  阅读(209)  评论(0编辑  收藏  举报