一起聊聊JVM中的垃圾收集器

开发 前端
CMS开始并发的清除已经被判断为已经死亡的对象,释放内存空间。这个阶段同样是与应用程序并行执行的,不会停止应用线程。

   堆上的对象被判断未无用对象之后,JVM就会执行垃圾清理来释放内存给后续的创建新对象使用。

图片图片

    垃圾收集算法(标记-清除算法、标记-复制算法、标记-整理算法)是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现,下图是市面上常见的垃圾收集器:

图片图片

    新生代收集器:Serial、ParNew、Parallel Scavenge;老年代收集器:SerialOld、CMS、Parallel Old;同时支持新生代和老年代收集器:G1、ZGC、Shenandoah。

    JDK9之前是新生代和老年代搭配使用;JDK1.9开始默认是G1垃圾收集器。在G1之前是分成新生代和老年代,新生代和老年代使用不同的垃圾收集器,因为新生代和老年代回垃圾的场景不同,所以使用不同的垃圾收集器。

1、新生代垃圾收集器

1.1 serial垃圾收集器

    Serial收集器是历史最悠久也是最基础垃圾收集器,它是一个单线程工作的收集器,其在进行垃圾收集时必须暂停其他所有工作线程(用户线程)直到垃圾收集结束,这也就是STW(stop the world)。

    STW会造成在用户正常使用系统时候把用户的正常工作的线程全部停掉,进而造成卡顿的现象给用户的体验是相当不好的。如下是Serial收集器工作的原理图:

图片图片

    垃圾回收不是想什么时候做就可以立即触发的,而是需要等待所有的线程运行到安全点后才可以触发。

    安全点是指代码中一些特定的位置,当线程运行到这些位置时它的状态才是确定的,这样JVM就可以安全的进行一些操作(如GC)。安全点位置主要有:

(1)方法返回之前

(2)调用某方法之后

(3)抛出异常的位置

(4)循环的末尾

    当垃圾收集需要中断线程的时候不是直接对线程操作,仅仅是简单的设置一个标志位,各个线程执行过程时会不停的主动去轮询这个标志,一旦发现中断标识为true时就会自己在最近的安全点上主动中断挂起。轮询标志的地方和安全点是重合的。

    假设一个线程处于sleep或中断状态,那么此时它就不能响应JVM的中断请求来运行到安全点,因此JVM引入了safe Region(在一段代码片段中引用关系不会发生变化,这个区域内的任意地方开始GC都是安全的)

    serial垃圾收集器采用的是复制算法,其特点是简单而高效,但是如果内存很大的时候,单线垃圾收集就会使得STW时间变得很长。

1.2 ParNew垃圾收集器

    ParNew收集器实质上是Serial收集器的多线程并行版本,除了同时使用多条线程进行垃圾收集之外,ParNew和Serial收集器可用的所有控制参数(如:-XXSurvivorRatio)、收集算法、对象分配规则、回收策略等都完全一致,如下是 ParNew收集器得工作原理图:

图片图片

    ParNew收集器是运行在Server模式下的虚拟机的首选垃圾收集器,ParNew除了Serial收集器搭配使用外,它能与CMS收集器配合工作。

1.3 Parallel Scavenge收集器

    Parallel Scavenge收集器是基于标记-复制算法实现的收集器,也是能够并行收集的多线程收集器,如下是其工作得原理图:

图片图片

    Parallel Scavenge收集器的目标是达到一个可控制的吞吐量(Throughput)。吞吐量是处理器用于运行用户代码的时间与处理器总消耗时间的比值,如下是其计算得方式:

吞吐量= 运行用户代码时间 / (运行用户代码时间+运行垃圾收集时间)

    如虚拟机需要完成任务A,用户代码加上垃圾收集总共耗费了10分钟,其中垃圾收集花掉1分钟,那吞吐量就是90%。

    另外Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量,如下所示:

参数

含义

-XX:MaxGCPauseMillis

控制最大垃圾收集停顿时间,允许的值是一个大于0的毫秒数,收集器将尽力保证内存回收花费的时间不超过用户设定值

 -XX:GCTimeRatio

设置吞吐量大小,值应当是一个大于0和小于100的整数,也就是垃圾收集时间占总时间的比率

    缩短垃圾收集停顿时间是以牺牲吞吐量和新生代空间为代价换取的,系统把新生代调得小一些,收集100MB新生代肯定比收集1G空间快,但是这样回导致垃圾收集发生得更频繁,如原来60秒收集一次、每次停顿100毫秒,现在变成20秒收集一次、每次停顿 50毫秒,这样停顿时间的确在下降,但吞吐量也降下来了。

    直接设置吞吐量大小,相当于吞吐量的倒数。如设置GCTimeRatio为 99,那么就允许最大1%的垃圾收集时间。

    Parallel Scavenge收集器与吞吐量关系密切,所以它也被称作“吞吐量优先收集器”。

2、老年代垃圾收集器

2.2 serial old垃圾收集器

    Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法。如下是 Serial Old收集器工作原理:

图片图片

     Serial Old收集器供客户端模式下的HotSpot虚拟机使用;在服务端模式下,主要的用途如下:

(1)在JDK5以及之前的版本中与Parallel Scavenge 收集器搭配使用,(2)作为CMS收集器发生失败时的后备预案,即就是在并发收集发生Concurrent Mode Failure时使用。

2.2 Parallel Old 收集器

    Parallel Old是Parallel Scavenge收集器的老年代版本,它是基于标记-整理算法实现的多线程并发收集,如下是其工作的原理图:

图片图片

    在注重吞吐量或CPU资源的场景下,都可以优先考虑Parallel Scavenge加Parallel Old 收集器组合来进行垃圾收集。由于Parallel Old底层采用的标记-整理算法,此时STW时间更长一些,如下是标记-整理算法的工作示意图:

图片图片

    垃圾标记出来后,不但要清除垃圾而且还要将存活的对象移动整理到内存的一侧。

2.3 CMS

    CMS(Concurrent Mark Sweep)基于标记-清除算法实现的,它以减少停顿时间(STW)为目标的垃圾收集器,主要用于处理老年代的垃圾回收任务,如下是CMS的工作原理图:

图片图片

    CMS垃圾收集器执行工作过程相对于其他垃圾收集器来说要更复杂一些,整个过程分为如下的几个过程:

(1)初始标记

    标记GC Roots能直接关联到的对象,虽然速度很快,但是这个过程会发生STW。

(2)并发标记

    从GC Roots 的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行。

(3)重新标记

    为了修正并发标记期间,因用户序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段也会暂停应用线程,但耗时较短。

(4)并发清除

    CMS开始并发的清除已经被判断为已经死亡的对象,释放内存空间。这个阶段同样是与应用程序并行执行的,不会停止应用线程。

    CMS垃圾收集器具有并发收集、低停顿的特点,但是CMS垃圾收集器对CPU资源敏感、同时CMS使用的是标记-清除算法清理垃圾,容易产生内存碎片。

    CMS在并发阶段并没有STW,所以用户线程可能会产生更多垃圾,此时就可能出现“Concurrent Mode Failure”失败进而导致一次STW的Full GC,然后使用serial old垃圾回收器收集垃圾。

总结:

(1)新生代的垃圾回收器有serial、ParNew、Parallel Scavenge;老年大垃圾收集器有Serial Old、CMS、Parallel Old;新生代和老年代收集器:G1、ZGC、Shenandoah。

(2)JDK8中默认的垃圾收集器组合是Parallel Scavenge+Parallel Old;JDK9默认是G1垃圾收集器。

(3)CMS是基于标记-清除算法实现的,Parallel Old基于标记-整理算法实现的。

(4)JDK8中ParNew+CMS组合适用于需要低停顿时间的应用,典型的应用如电商网站、在线游戏、高并发服务器。

(5)JDK8中Parallel Scavenge+Parallel Old适用于多核处理器的高吞吐量应用,如科学计算、数据分析、大规模数据处理等场景。


责任编辑:武晓燕 来源: 龙虾编程
相关推荐

2024-08-26 08:58:50

2017-09-21 14:40:06

jvm算法收集器

2022-04-19 11:25:31

JVMZGC垃圾收集器

2024-03-14 09:00:00

2011-12-20 10:43:21

Java

2022-07-25 10:15:29

垃圾收集器Java虚拟机

2011-07-21 14:54:26

java垃圾收集器

2020-02-25 22:01:36

理解JVM垃圾收集器

2009-10-30 10:47:48

VB.NET垃圾收集器

2023-11-16 08:00:56

Java11G1

2013-12-19 09:46:04

垃圾收集器

2024-03-15 08:04:30

G1CMSJVM

2024-03-27 10:27:35

延迟垃圾收集器

2011-05-10 16:04:45

Java垃圾收集器

2010-09-27 12:42:15

JVM1.4.1JVM垃圾收集

2021-08-15 18:59:13

垃圾收集器JDK

2015-06-17 14:10:52

OracleJava 9垃圾收集器

2024-12-03 09:01:33

2010-01-14 11:28:54

JVM分代垃圾回收

2022-01-20 10:34:49

JVM垃圾回收算法
点赞
收藏

51CTO技术栈公众号