Java之JVM的深入探究(五)--垃圾回收器

萝らか妹 提交于 2021-01-09 21:48:34

预计阅读时间: 9分钟


    我们在上一篇中已经详细的探究了关于JVM的垃圾回收的几种算法,从中我们知道了不同的算法有这不同的优缺点的,然而实际在Java的JVM的并不是单纯的使用其中的某一种算法来实现垃圾回收的,而是将不同的垃圾回收算法包装在不同的垃圾回收器当中,这样用户就可以根据自身的需求,有选择性的使用不同的垃圾回收器,以便让自己的java程序性能到达最佳。

垃圾回收器概述:垃圾收集器是垃圾回收算法(标记-清除算法、复制算法、标记-整理算法、分代收集算法等)的具体实现,不同商家、不同版本的JVM所提供的垃圾收集器可能会有很在差别,本文主要介绍HotSpot虚拟机中的垃圾收集器。


在探究垃圾回收器之前我们先来回忆下之前探究过的关于JVM内存模型中的Java堆的知识,先来看一下Java堆的结构图如下:

java堆内存结构包括:新生代和老年代,其中新生代由一个伊甸区和2个幸存区组成,2个幸存区是大小相同,完全对称的,没有任何差别。我们把它们称为S0区和S1区,也可以称为from区和to区。


JVM的垃圾回收主要是针对以上堆空间的垃圾回收,当然其实也会针对元数据区(永久区)进行垃圾回收,在此我们主要针对堆内存空间的垃圾回收进行探究。

垃圾收集器组合:


下面我们介绍几种常见的垃圾回收器:


1、>>串行收集器:

(1)特点: 
  –它仅仅使用单线程进行垃圾回收 
  –它是独占式的垃圾回收 
  –进行垃圾回收时, Java应用程序中的线程都需要暂停(Stop-The-World) 
  –适合CPU等硬件不是很好的场合

       –对新生代的回收使用复制算法,对老年代使用标记压缩算法,这也和之前介绍的垃圾回收算法优势是相合拍的 

       –是最古老最稳定的收集器,尽管它是串行回收,回收时间较长,但其稳定性是优于其他回收器的,综合来说是一个不错的选择。  

(2)设置参数: 
  -XX:+UseSerialGC 指定新生使用新生代串行收集器和老年代串行收集器, 当以client模式运行时, 它是默认的垃圾收集器

串行回收器的执行流程如下所示:


2、>>并行回收器:

顾名思义,并行回收器就是使用多线程并行回收,不过这里需要注意的是,针对新生代和老年代,是否都使用并行,有不同的回收器选择:


2.1、 ParNew回收器:

(1)特点: 
  –这个回收器只针对新生代进行并发回收,老年代依然使用串行回收 
  –回收算法依然和串行回收一样,新生代使用复制算法,老年代使用标记压缩算法
  –垃圾回收时, 应用程序仍会暂停, 只不过由于是多线程回收, 在多核CPU上,回收效率会高于串行回收器, 反之在单核CPU, 效率会不如串行回收器 
(2)设置参数: 

        -XX:+UseParNewGC  如果要进一步指定并发的线程数,可以配置一下参数:

        -XX:ParallelGCThreads

串行回收器的执行流程如下所示:


2.2、Parallel回收器:

依然是并行回收器,但这种回收器有两种配置

一种类似于ParNEW:

(1)特点: 
  –新生代使用并行回收、老年代使用串行回收 
  –它与ParNew的不同在于它在设计目标上更重视吞吐量 
  –可以认为在相同的条件下它比ParNew更优。 
(2)设置参数: 

        -XX:+UseParallelGC

另外一种配置则不同于ParNew:

(1)特点: 
  –对于新生代和老年代均适应并行回收  
(2)设置参数: 

        -XX:+UseParallelOldGC

Parallel回收器的流程和ParNew的流程是一致的,如下图:


3、>>CMS回收器(Concurrent Mark Sweep,并发\标记清除):

CMS回收器: Concurrent Mark Sweep,并发标记清除。注意这里注意两个词:并发、标记清除。

(1)特点: 
  –是可以与应用程序并发执行、交替执行, 非独占式的回收器, 大部分时候应用程序不会停止运行 
  –是一种针对老年代的回收器,不对新生代产生作用 
  –标记清除表示这种回收器不是使用的是标记整理算法,这和前面介绍的串行回收器和并发回收器有所不同, 因此回收后会有内存碎片, 可以使参数设置进行内存碎片的压缩整理  

       –与ParallelGC和ParallelOldGC不同, CMS主要关注系统停顿时间 ,这种回收器优点在于减少了应用程序停顿的时间,因为它不需要应用程序完成暂定等待垃圾回收,而是与垃圾回收并发执行。

(2)CMS主要步骤: 
  1. 初始标记 :标记从GC Root可以直接可达的对象;

  2. 并发标记(应用程序线程一起) :主要标记过程,标记全部对象;

  3. 预清理 
  4. 重新标记 :由于并发标记时,用户线程依然运行,因此在正式清理前,再做依次重新标记,进行修正。

  5. 并发清理(用户线程一起) :基于标记结果,直接清理对象。

  6. 并发重置

–>注:初始标记与理新标记是独占系统资源的,不能与用户线程一起执行,而其它阶段则可以与用户线程一起执行 
(3)设置参数: 
  -XX:+UseConcMarkSweepGC 老年代使用CMS回收器, 新生代使用ParNew回收器 

流程如下图所示:


注:初始标记与重新标记是独占系统资源的,不能与用户线程一起执行,而其它阶段则可以与用户线程一起执行 。

从上图可以看到标记过程分三步:初始标记、并发标记、重新标记。并发标记是最主要的标记过程,而这个过程是并发执行的,可以与应用程序线程同时进行,初始标记和重新标记虽然不能和应用程序并发执行,但这两个过程标记速度快,时间短,所以对应用程序不会产生太大的影响。最后并发清除的过程,也是和应用程序同时进行的,避免了应用程序的停顿。


CMS的优缺点:

优点:就是减少了应用程序的停顿时间,让回收线程和应用程序线程可以并发执行。但它也不是完美的,

缺点:从他的运行机制可以看出,因为它不像其他回收器一样集中一段时间对垃圾进行回收,并且在回收时应用程序还是运行,因此它的回收并不彻底。这也导致了CMS回收的频率相较其他回收器要高,频繁的回收将影响应用程序的吞吐量。


4、>>G1回收器(jdk1.7后全新的回收器, 试图用于取代CMS):

G1回收器是jdk1.7以后推出的回收器,试图取代CMS回收器。

不同于其他的回收器、G1将堆空间划分成了互相独立的区块。每块区域既有可能属于老年代、也有可能是新生代,并且每类区域空间可以是不连续的(对比CMS的老年代和新生代都必须是连续的)。这种将老年代区划分成多块的理念源于:当并发后台线程寻找可回收的对象时、有些区块包含可回收的对象要比其他区块多很多。虽然在清理这些区块时G1仍然需要暂停应用线程、但可以用相对较少的时间优先回收包含垃圾最多区块。这也是为什么G1命名为Garbage First的原因:第一时间处理垃圾最多的区块。


(1)特点: 
  –独特的垃圾回收策略, 属于分代垃圾回收器
  –使用分区算法, 不要求eden, 年轻代或老年代的空间都连续 
  –并行性: 回收期间, 可由多个线程同时工作, 有效利用多核cpu资源 
  –并发性: 与应用程序可交替执行, 部分工作可以和应用程序同时执行, 
  –分代GC: 分代收集器, 同时兼顾年轻代和老年代 
  –空间整理: 回收过程中, 会进行适当对象移动, 减少空间碎片 
  –可预见性: G1可选取部分区域进行回收, 可以缩小回收范围, 减少全局停顿 
(2)设置参数: 
  -XX:+UseG1GC  打开G1收集器开关, 

G1相对CMS回收器来说主要优点在于:

1、因为划分了很多区块,回收时减小了内存碎片的产生;

2、G1适用于新生代和老年代,而CMS只适用于老年代。


到此,JVM垃圾回收器的探究已完毕,下面列出这些常使用的垃圾回收器配置参数的快照:

-XX:+UseSerialGC

在新生代和老年代使用串行收集器

-XX:+UseParNewGC

在新生代使用并行收集器

-XX:+UseParallelGC

新生代使用并行回收收集器,更加关注吞吐量

-XX:+UseParallelOldGC

老年代使用并行回收收集器

-XX:ParallelGCThreads

设置用于垃圾回收的线程数

-XX:+UseConcMarkSweepGC

新生代使用并行收集器,老年代使用CMS+串行收集器

-XX:ParallelCMSThreads

设定CMS的线程数量

-XX:+UseG1GC

启用G1垃圾回收器


更多技术文章,扫描下方二维码关注我!!!


本文分享自微信公众号 - 一只蓝色猿(umizhang0910)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!