JVM GC垃圾回收器详解

3 篇文章 0 订阅



https://www.cnblogs.com/jiling/p/8527050.html

 

一、GC 算法与种类

  1. GC的概念

    GC的全称是Garbage Collection (垃圾收集),java并不是最早使用GC概念的语言,早在1960年 List语言就使用了GC,java借鉴了该语言GC的实现,实现了垃圾回收机制。

    在Java中,GC的对象是堆空间和永久区。

     

    2、GC算法介绍

    2.1、引用计数法

    应用计数器老牌垃圾回收算法,通过引用计算来回收垃圾,使用者有COM、ActionScript3、Python,java并没有使用该算法,原因是应用计数器算法对性能有有一定影响,且没法处理循环引用。

    引用计数器的实现很简单,对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器就减1。只要对象A的引用计数器的值为0,则对象A就不可能再被使用。

     

    引用计数法的问题

    引用和去引用伴随加法和减法,影响性能

    很难处理循环引用

     

     

    2.2、标记清除

    标记-清除算法是现代垃圾回收算法的思想基础。标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。一种可行的实现是,在标记阶段,首先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象。然后,在清除阶段,清除所有未被标记的对象。

     

    2.3、标记压缩标记整理

    标记-压缩算法适合用于存活对象较多的场合,如老年代。它在标记-清除算法的基础上做了一些优化。和标记-清除算法一样,标记-压缩算法也首先需要从根节点开始,对所有可达对象做一次标记。但之后,它并不简单的清理未标记的对象,而是将所有的存活对象压缩到内存的一端。之后,清理边界外所有的空间。

    相对于标记-清除算法,标记压缩算法能更好的使用空间,标记清除算法会产生大量碎片空间,可能会导致大对象存放时空间不足。

     

    2.4、复制算法

    与标记-清除算法相比,复制算法是一种相对高效的回收方法,不适用于存活对象较多的场合,如老年代。

    将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。

     

     

     

    用于复制的两块相同大小空间也叫S0、S1(分别对应途中from、to区域),也就是幸存者区域,上图total(13824k=12288k+1536k)表示可用的总空间,实际分配空间是15M,剩下的1536K是由于使用了复制算法浪费的空间。

     

    在jvm中使用了分代思想,依据对象的存活周期进行分类,短命对象归为新生代,长命对象归为老年代。根据不同代的特点,选取合适的收集算法,对于少量对象存活区(伊甸区),适合复制算法;大量对象存活(老年区),适合标记清理或者标记压缩

     

  1. 可触及性

    3.1、可触及的

    从根节点可以触及到这个对象,即从根节点可以遍历到对这个对象的引用。

    一般可以是:

    栈中引用的对象、方法区中静态成员或者常量引用的对象(全局对象)、JNI方法栈中引用对象。

     

    经验:避免使用finalize(),操作不慎可能导致错误。

    优先级低,何时被调用, 不确定

    何时发生GC不确定

    可以使用try-catch-finally来替代它

     

    3.2、可复活的

    一旦所有引用被释放,就是可复活状态,因为在finalize()中可能复活该对象

    3.3、不可触及的

    在finalize()后,可能会进入不可触及状态,不可触及的对象不可能复活可以回收

     

    4、Stop-The-World

    Java中一种全局暂停的现象,所有Java代码停止,native代码可以执行,但不能和JVM交互。多半由于GC引起Dump线程,死锁检查,堆Dump

    危害长时间服务停止,没有响应;遇到HA系统,可能引起主备切换,严重危害生产环境。

二、垃圾收集器

Java虚拟机种类有多种,下面以Sun的HotSpot 为例说明。

HotSpot JVM收集器

 

              上面有7中收集器,分为两块,上面为新生代收集器,下面是老年代收集器。如果两个收集器之间存在连线,就说明它们可以搭配使用。

1 GC收集器

1.1 Serial串行收集器

串行收集器主要有两个特点:第一,它仅仅使用单线程进行垃圾回收;第二,它独占式的垃圾回收。

在串行收集器进行垃圾回收时,Java 应用程序中的线程都需要暂停(“StopThe World”),等待垃圾回收的完成,这样给用户体验造成较差效果。虽然如此,串行收集器却是一个成熟、经过长时间生产环境考验的极为高效的收集器。新生代串行处理器使用复制算法,实现相对简单,逻辑处理特别高效,且没有线程切换的开销。在诸如单 CPU 处理器或者较小的应用内存等硬件平台不是特别优越的场合,它的性能表现可以超过并行回收器和并发回收器。在 HotSpot 虚拟机中,使用-XX:+UseSerialGC 参数可以指定使用新生代串行收集器和老年代串行收集器。

-  新生代、老年代使用串行回收。

-  新生代使用复制算法。

-  老年代使用标记-压缩算法。

 

新生代日志输出:

0.844: [GC 0.844: [DefNew: 17472K->2176K(19648K), 0.0188339secs] 17472K->2375K(63360K), 0.0189186 secs] [Times: user=0.01 sys=0.00,real=0.02 secs]

8.259: [Full GC 8.259: [Tenured: 43711K->40302K(43712K),0.2960477 secs] 63350K->40302K(63360K), [Perm : 17836K->17836K(32768K)],0.2961554 secs]

[Times: user=0.28 sys=0.02, real=0.30 secs]

 

1.2 ParNew并行收集器

并行收集器是工作在新生代的垃圾收集器,它只简单地将串行回收器多线程化。它的回收策略、算法以及参数和串行回收器一样。并行回收器也是独占式的回收器,在收集过程中,应用程序会全部暂停。

但由于并行回收器使用多线程进行垃圾回收,因此,在并发能力比较强的 CPU上,它产生的停顿时间要短于串行回收器,而在单 CPU 或者并发能力较弱的系统中,并行回收器的效果不会比串行回收器好,由于多线程的压力,它的实际表现很可能比串行回收器差。开启并行回收器可以使用参数-XX:+UseParNewGC,该参数设置新生代使用并行收集器,老年代使用串行收集器。使用-XX:ParallelGCThreads 限制线程数量。

 

ParNew并行收集器日志输出:

0.834:[GC 0.834: [ParNew:13184K->1600K(14784K), 0.0092203 secs] 13184K->1921K(63936K), 0.0093401secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

 

1.3 Parallel平行收集器

Parallel收集器类假于ParNew并行收集器,它使用复制算法的收集器。从表面上看,它和并行收集器一样都是多线程、独占式的收集器。但是,并行回收收集器有一个重要的特点:它非常关注系统的吞吐量。

Parallel收集器可以使用以下参数启用:

-XX:+UseParallelGC:新生代使用并行回收收集器,老年代使用串行收集器。

-XX:+UseParallelOldGC:新生代和老年代都是用并行回收收集器。

 

这两个参数是矛盾的。因为停顿时间和吞吐量不可能同时调优:

-XX:MaxGCPauseMills:最大停顿时间,单位毫秒。GC尽力保证回收时间不超过设定值。

-XX:GCTimeRatio:垃圾收集时间占总时间的比,0-100的取值范围,默认99,即最大允许1%时间做GC。

 

 

        Parallel收集器日志输出:

1.500:[Full GC [PSYoungGen:2682K->0K(19136K)] [ParOldGen: 28035K->30437K(43712K)] 30717K->30437K(62848K) [PSPermGen:10943K->10928K(32768K)], 0.2902791 secs] [Times: user=1.44 sys=0.03,real=0.30 secs]

 

1.4 CMS 收集器

CMS 是 Concurrent Mark Sweep 的缩写,意为并发标记清除,从名称上可以得知,它使用的是标记-清除算法,同时它又是一个使用多线程并发回收的垃圾收集器。与并行回收收集器不同,CMS 收集器主要关注于系统停顿时间。

CMS 工作时,主要步骤有:

初始标记(CMS initial mark): 根可以直接关联到的对象,速度比较快。

并发标记(CMS concurrent mark):主要标记过程,标记全部对象。注:和用户线程一起并发运行。

重新标记(CMS remark): 由于并发标记时,用户线程依然运行,因此在正式清理前,再做修正。

并发清除和并发重置(CMS concurrent sweep): 基于标记结果,直接清理对象。

其中初始标记和重新标记是独占系统资源的,而并发标记、并发清除和并发重置是可以和用户线程一起执行的。因此,从整体上来说,CMS 收集不是独占式的,它可以在应用程序运行过程中进行垃圾回收。

 

CMS 收集器在其主要的工作阶段虽然没有暴力地彻底暂停应用程序线程,但是由于它和应用程序线程并发执行,相互抢占 CPU,所以在 CMS 执行期内对应用程序吞吐量造成一定影响。CMS 默认启动的线程数是(ParallelGCThreads+3)/4),ParallelGCThreads 是新生代并行收集器的线程数,也可以通过-XX:ParallelCMSThreads 参数手工设定 CMS的线程数量。当 CPU 资源比较紧张时,受到 CMS收集器线程的影响,应用程序的性能在垃圾回收阶段可能会非常糟糕。

标记-清除算法将会造成大量内存碎片,离散的可用空间无法分配较大的对象。在这种情况下,即使堆内存仍然有较大的剩余空间,也可能会被迫进行一次垃圾回收,以换取一块可用的连续内存,这种现象对系统性能是相当不利的,为了解决这个问题,CMS 收集器还提供了几个用于内存压缩整理的算法。

-XX:+UseCMSCompactAtFullCollection参数可以使 CMS 在垃圾收集完成后,进行一次内存碎片整理。内存碎片的整理并不是并发进行的。

-XX:CMSFullGCsBeforeCompaction参数可以用于设定进行多少次 CMS 回收后,进行一次内存压缩。

-XX:ParallelCMSThreads设定CMS的线程数量。

 

 

CMS收集器日志输出:

1.662: [GC [1 CMS-initial-mark: 28122K(49152K)] 29959K(63936K), 0.0046877 secs] [Times:user=0.00 sys=0.00, real=0.00 secs]

1.666: [CMS-concurrent-mark-start]

1.699: [CMS-concurrent-mark: 0.033/0.033 secs] [Times: user=0.25 sys=0.00, real=0.03 secs]

1.699: [CMS-concurrent-preclean-start]

1.700: [CMS-concurrent-preclean: 0.000/0.000 secs] [Times:user=0.00 sys=0.00, real=0.00 secs]

1.700: [GC[YG occupancy: 1837 K (14784 K)]1.700: [Rescan(parallel) , 0.0009330 secs]1.701: [weak refs processing, 0.0000180 secs]

[1 CMS-remark:28122K(49152K)] 29959K(63936K), 0.0010248 secs] [Times: user=0.00 sys=0.00,real=0.00 secs]

1.702: [CMS-concurrent-sweep-start]

1.739: [CMS-concurrent-sweep: 0.035/0.037 secs] [Times: user=0.11 sys=0.02, real=0.05 secs]

1.739: [CMS-concurrent-reset-start]

1.741:[CMS-concurrent-reset:0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

 

CMS特点:

-  尽可能降低停顿

-  会影响系统整体吞吐量和性能

比如,在用户线程运行过程中,分一半CPU去做GC,系统性能在GC阶段,反应速度就下降一半

-  清理不彻底

   因为在清理阶段,用户线程还在运行,会产生新的垃圾,无法清理。  

   因为和用户线程一起运行,不能在空间快满时再清理。

   -XX:CMSInitiatingOccupancyFraction设置触发GC的阈值

   如果不幸内存预留空间不够,就会引起concurrent mode failure,产生这种问题,通常都使用串行收集器再次垃圾收集,在这种情况下内存基本被占用满了,通常GC停顿的时间较长。

 

CMS收集器日志输出:

33.348: [Full GC 33.348: [CMS33.357: [CMS-concurrent-sweep:0.035/0.036 secs] [Times: user=0.11 sys=0.03, real=0.03 secs]

 (concurrentmode failure): 47066K->39901K(49152K), 0.3896802 secs]60771K->39901K(63936K), [CMS Perm : 22529K->22529K(32768K)], 0.3897989secs] [Times: user=0.39 sys=0.00, real=0.39 secs]

 

1.5 G1收集器(Garbage First)

G1收集器与前面的CMS收集器相比有两个显著的改进:一是G1收集器是基于“标记-整理”算法实现的收集器,也就是说它不会产生空间碎片,这对于长时间运行的应用系统来说非常重要。二是它可以非常精确地控制停顿,既能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时Java(RTSJ)的垃圾收集器的特征了。G1收集器可以实现在基本不牺牲吞吐量的前提下完成低停顿的内存回收,这是由于它能够极力地避免全区域的垃圾收集,之前的收集器进行收集的范围都是整个新生代或老年代,而G1将整个Java堆(包括新生代、老年代)划分为多个大小固定的独立区域(Region),并且跟踪这些区域里面的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间,优先回收垃圾最多的区域(这就是Garbage First名称的来由)。区域划分及有优先级的区域回收,保证了G1收集器在有限的时间内可以获得最高的收集效率。

 

2 GC收集器参数

2.1 收集器参数总结

1、 Serial串行收集器相关的参数 

-XX:+UseSerialGC

新生代和老年代使用串行回收器。

-XX:SurvivorRatio

设置eden区大小和survivior区大小的比例。

-XX:PretenureSizeThreshold

设置大对象直接进入老年代的阈值。当对象的大小超过这个值时,将直接在老年代分配。

-XX:MaxTenureSizeThreshold

设置对象进入老年代的年龄的最大值。每一次Minor

 

2、 ParNew并行收集器相关的参数

-XX:+UseParNewGC

在新生代使用并行收集器。

-XX:+UseParallelGC

新生代使用并行回收收集器

-XX:+UseParallelOldGC

老年代使用并行回收器。

-XX:ParallelGCThreads

设置用于垃圾回收的线程数。通常情况下可以和CPU数量相等,但在CPU数量比较多的情况下,设置相对较小的数值也是合理的。

-XX:MaxGCPauseMills

设置最大垃圾收集停顿时间。它的值是一个大于0的正数。收集器在工作时,会调整java堆大小或者其他一些参数,尽可能地把停顿时间控制在MaxGCPauseMills以内。

-XX:GCTimeRatio

设置吞吐量大小。它的值时一个0到100之间的整数。假设GCTimeRatio的值为n,那么系统将花费不超过1/(1+n)的时间用于垃圾收集。

-XX:+UseAdaptiveSizePolicy

打开自适应GC策略。在这种模式下,新生代的大小、eden和survivior的比例、晋升老年代的对象年龄等参数会被自动调整,以达到在堆大小、吞吐量和停顿时间之间的平衡点。

 

3、 CMS 收集器相关的参数

-XX:+UseConcMarkSweepGC

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

-XX:ParallelCMSThreads

设定CMS的线程数量。

-XX:CMSInitiatingOccupancyFraction

设置CMS收集器在老年代空间被使用多少后触发,默认为68%。

-XX:+UseCMSCompactAtFullCollection

设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片整理。

-XX:CMSFullGCsBeforeCompaction

设定进行多少次CMS垃圾回收后,进行一次内存压缩。

-XX:+CMSClassUnloadingEnabled

允许对类元数据区进行回收。

-XX:CMSInitiatingPermOccupancyFraction

当永久区占用率达到这一百分比时,启动CMS回收(前提是-XX:+CMSClassUnloadingEnabled激活了)。

-XX:CMSInitiatingPermOccupancyOnly

表示只在到达阈值的时候才进行CMS回收。

-XX:+CMSIncrementalMode

使用增量模式,比较适合单CPU。增量模式在JDK8中标记为废弃,并且将在JDK9中彻底移除。

 

4、 G1收集器相关的参数

-XX:+UseG1GC

使用G1回收器。

-XX:MaxGCPauseMillis

设置最大垃圾收集停顿时间。

-XX:GCPauseIntervalMillis

设置停顿间隔时间。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值