JVM相关知识总结:聊聊JVM的垃圾收集(二)

前言:

上一节我们介绍了JVM内存模型,常见的垃圾收集算法以及新生代的垃圾收集器。这篇文章会进一步总结老年代的垃圾收集器,如何选择合适的垃圾收集器以及JVM参数调优。

一、老年代垃圾收集器

1.1 Serial Old垃圾收集器

Serial Old收集器是工作在老年代的单线程垃圾收集器,跟Serial不同的是采用标记整理算法。

使用场景: 在Client模式下可以和Serial收集器配合使用,如果使用Server模式,可以和Parallel Scavenge收集器配合使用。另外一种是作为CMS的后备预案,在发生Concurrent Mode Failure使用。

收集过程:

 1.2 Parallel Old

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程的标记—整理算法进行垃圾回收,更加注重吞吐量。

使用场景: 为了替代Serial Old和Parallel Scanvenge配合使用

收集过程:

1.3 CMS= Concurrent Mark Sweep

CMS收集器是一种以获取最短停顿时间为目标的收集器,采用标记—清除算法。适用于希望系统停顿时间短,给用户更好的体验场景。

回收步骤:

  1. 初始标记: 标记GC Roots能直接关联的对象。存在STW;
  2. 并发标记: GC Roots Tracing,可以和用户线程并发执行,不会存在STW;
  3. 重新标记: 标记期间产生的对象存活的再次判断,修正对这些对象的标记,执行时间相对于并发标记短,存在STW;
  4. 并发清除: 清除垃圾对象,可以和用户线程并发执行。

CMS收集器缺点:

  • 对CPU资源比较敏感,因为存在并发标记和并发清除的过程;
  • 无法处理浮动垃圾。可能出现Concurrent Mode Failure而导致另一次Full GC的产生,由于在并发清理时,用户线程还在运行,所以在这个期间新的垃圾会不断产生,这部分就是所谓的浮动垃圾,只能在下一次GC时再清理;
  • 采用标记—清除算法,所以会产生垃圾碎片。内存碎片导致大对象无法分配在连续的内存空间,可能会触发Full GC,影响应用的性能。

1.4 G1收集器

从 JDK 9 开始,JVM 的默认垃圾回收器就从 Parallel GC 调整为 G1,并且开始全面废除 CMS 。限制或者减少 GC 停顿时间相比系统吞吐量而言更加重要,从 PGC 切换至低延迟的 G1 能够为大部分用户带来更好的体验。G1 的性能在 JDK 8 以及后续的 release 版本都得到了极大的优化,G1 是一个具备所有 GC 特性的垃圾回收器,因此将 G1 设置为 JVM 默认的 GC。根据 JEP-291 中的说明,为了减轻 GC 代码的维护负担以及加速新功能开发,决定在 JDK 9 中废弃 CMS GC。

G1垃圾收集器的内存分区不再采用传统的内存分区,将新生代,老年代的物理空间划分取消。取而代之的是,把堆内存分成若干个Region,每次收集的时候,只收集其中几个区域,以此来控制垃圾回收产生的STW。和传统垃圾收集器最大的区别在于,弱化了分代概念,引入了分区的思想。

收集步骤:

  1. 初始标记。收集所有GC根,会存在STW,在年轻代完成;
  2. 并发标记,标记存活的对象;
  3. 最终标记,最后一个标记阶段,STW,时间很多,完成所有标记工作
  4. 筛选回收,回收没有存活对象的Region并加入可用Region队列。

收集过程示意图:

 使用算法: 局部上使用复制算法,整体上使用标记—整理算法。

优点:

「并行与并发」:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短Stop-The-World停顿时间。部分收集器原本需要停顿Java线程来执行GC动作,G1收集器仍然可以通过并发的方式让Java程序继续运行。

「分代收集」:G1能够独自管理整个Java堆,并且采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。

「空间整合」:G1运作期间不会产生空间碎片,收集后能提供规整的可用内存。

「可预测的停顿」:G1除了追求低停顿外,还能建立可预测的停顿时间模型。能让使用者明确指定在一个长度为M毫秒的时间段内,消耗在垃圾收集上的时间不得超过N毫秒。

1.5 ZGC

  JDK11加入,目前还在实验阶段,在这里不做详细介绍。

 

二、垃圾收集器之间的关系

新生代: Serial,ParNew, Parallel Scanvenge

老年代: Serial Old, Parallel Old, CMS

整堆收集器: G1, ZGC

三、 Minor GC/Major GC/ Full GC

 Minor GC:发生在年轻代的GC称为Minor GC。因为Java大多数对象都是朝生夕灭,所以Minor GC非常频繁,一般回收速度也比较快。

Major GC:发生在老年代,经常伴随至少一次Minor GC,Major GC速度一般比Minor GC慢十倍以上

Full GC: 发生在新生代+老年代。

四、触发Full GC条件

1. 调用System.gc

2. 老年代空间不足:

旧生代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出如下错误: java.lang.OutOfMemoryError: Java heap space 为避免以上两种状况引起的FullGC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。

3. 永久代空间满:

PermanetGeneration中存放的为一些class的信息等,当系统中要加载的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满,在未配置为采用CMS GC的情况下会执行Full GC。如果经过Full GC仍然回收不了,那么JVM会抛出如下错误信息: java.lang.OutOfMemoryError: PermGen space 为避免Perm Gen占满造成Full GC现象,可采用的方法为增大Perm Gen空间或转为使用CMS GC。

4.  统计得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间;

5. 由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。即老年代无法存放下新年代过度到老年代的对象的时候,会触发Full GC。

五、 新生代中Eden:S1:S2为什么是8:1:1

新生代中的可用内存:复制算法用来担保的内存为9:1,所以只会造成 10% 的空间浪费。可用内存中Eden:S1区为8:1 即新生代中Eden:S1:S2 = 8:1:1这个比例,是由参数 -XX:SurvivorRatio 进行配置的(默认为 8)。

 

六、 如何选择垃圾收集器?

不同的垃圾收集器都有其各自的优缺点,没有绝对的好坏,我们在实际中需要结合具体情况进行选择。以下是一些选择建议:

1. 如果你的堆大小不是很大,选择串行收集器一般效率最高。设置参数: -XX:+UseSerialGC

2. 如果应用运行在单核机器上,或者虚拟机被分配到的核数只有单核,选择串行依然是最合适的;

3. 如果应用是吞吐量优先,并且对较长时间的停顿没有特别要求,选择串行收集器比较好。设置参数-XX: +UseParallelGC

4. 如果你的应用对响应时间要求较高,想要较少的停顿。甚至 1 秒的停顿都会引起大量的请求失败,那么选择G1、ZGC、CMS都是合理的。虽然这些收集器的 GC 停顿通常都比较短,但它需要一些额外的资源去处理这些工作,通常吞吐量会低一些。参数:-XX:+UseConcMarkSweepGC、-XX:+UseG1GC、-XX:+UseZGC 等。

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值