回首掏-JVM垃圾收集器

导读:本篇文章讲解 回首掏-JVM垃圾收集器,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

回首掏-JVM垃圾收集器

本文将对下面几种GC回收器进行讲解,前面几个会简单的讲一下,后面两个会重点说的。
要是哪里有错误的地方,一定要指出哦,嘻嘻

  • Serial
  • Serial Old
  • ParNew
  • Parallel Scavenge
  • Parallel Old
  • CMS
  • G1

首先,我们来了解一下这几种收集器的原理、优缺点和相应的解决方法
STW就是Stop The World
新生代为啥选择复制算法,而老年代不选:

因为在新生代绝大多数的内存都是会被回收的,所以留下来的不需要回收的就很少了,所以复制算法更合适,而老年代中对象基本是比较大的,比如数组啥的,它们复制的开销比较大,不适合选择复制算法,而选择标记-整理、标记-清除算法是比较好的选择。

1、Serial

Serial收集器是发展最久的垃圾回收器,Serial单词本身的意思是“串行”。

  • 是一个单线程的收集器
  • 作用于新生代
  • 采用复制算法
  • 在进行垃圾回收的时候会产生STW

主要适用于单CPU环境下的Client模式,目标是追求响应速度优先,可以避免多线程上下文切换带来的消耗

2、Serial Old

Serial的老年代版本。

  • 即与Serial一样,采用单线程垃圾回收
  • 不同的是采用的标记-整理算法
  • 进行垃圾回收时会产生STW
  • 作用于老年代

主要适用于单CPU环境下的Client模式、CMS的后备预案,目标是追求响应速度优先

3、ParNew

ParNew是Serial的多线程版本。

  • 即使用多线程并行进行垃圾回收
  • 作用于新生代
  • 采用复制算法
  • 进行垃圾回收时会产生STW

主要适用于多CPU环境时在Server模式下与CMS配合,目标是追求响应速度优先

4、Parallel Scavenge

Parallel Scavenge与ParNew类似,只是关注点不同。Parallel Scavenge主要关注

  • 吞吐量。吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)。提供了两个参数来控制吞吐量

-XX:MaxGCPauseMillis 控制最大垃圾收集停顿时间,大于0的毫秒数
-XX:GCTimeRatio 设置吞吐量大小,大于0且小于100的整数,即垃圾收集时间占总时间的比率

  • 使用多线程并行进行垃圾回收
  • 作用于新生代
  • 采用复制算法
  • 进行垃圾回收时会产生STW

Parallel Scavenge收集器的目标是达到一个可控制的吞吐量(Throughput)
我们知道,停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验。
而高吞吐量则可以高效率地利用CPU时间,尽快完成程序的计算任务,主要适合在后台计算而不需要过多交互的任务。
主要适用于在后台运算而不需要太多交互的任务,目标是追求吞吐量优先

5、Parallel Old

Parallel Old是Parallel Scavenge的老年代版本

  • 多线程并行收集
  • 使用标记-整理算法
  • 作用于老年代
  • 进行垃圾回收时会产生STW

主要适用于在后台运算而不需要太多交互的任务,目标是追求吞吐量优先 ,JDK8中默认的选择是”-XX:+UseParallelGC”,是 Parallel Scavenge + Parallel Old组合。

6、CMS(Concurrent Mark Sweep)

  • 以获取最短回收停顿时间为目标
  • 作用于老年代
  • 使用标记-清除算法
  • 会产生空间碎片,但可以通过配置 -XX:+UseCmsCompactAtFullCollection (默认已开启)用于在CMS收集器要进行FullGC时开启内存碎片的整理

CMS特点: 并发收集,低停顿。

它的标记-清除算法过程:

  1. 初始标记,会导致STW
  2. 并发标记,与用户线程同时运行;
  3. 预清理,与用户线程同时运行;
  4. 可被终止的预清理,与用户线程同时运行;
  5. 重新标记 ,会导致STW
  6. 并发清除,与用户线程同时运行;

其实只要关注四个就差不多了,简单点就是:

  • 初始标记:只标记GC Roots直接关联到的对象,速度快,需要“Stop The World”。
  • 并发标记:根据GC Roots标记的对象进行追踪,标记出需要回收的对象。过程较长,但GC线程与用户线程并发执行,不产生 STW
  • 重新标记:修复并发标记过程中用户程序的运行导致标记的对象产生变动的的那部分对象,会产生STW
  • 并发清除:清除标记的对象,与用户线程并发执行,不产生STW

这里介绍一下一般可以作为GC Roots的对象是什么

虚拟机栈(栈帧中的局部变量表,Local Variable Table)中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中(native方法)引用的对象
回首掏-JVM垃圾收集器
上图从https://my.oschina.net/u/4349287/blog/3225344中来

CMS缺点:

  • 垃圾碎片的问题,我们都知道CMS是使用的是标记-清除算法的,所以不可避免的会出现垃圾碎片的问题
  • 一般CMS的GC耗时80%都在remark阶段,remark阶段停顿时间会很长,在CMS的这四个主要的阶段中,最费时间的就是重新标记阶段
  • concurrent mode failure: 也就是CMS收集器无法处理浮动垃圾(Floating Garbage)产生的, 这个异常发生在CMS正在回收的时候。执行CMS GC的过程中,同时业务线程也在运行,当年轻带空间满了,执行Young GC时,需要将存活的对象放入到老年代,而此时老年代空间不足,这时CMS还没有机会回收老年带产生的,或者在做Minor GC的时候,新生代救助空间放不下,需要放入老年代,而老年代也放不下而产生的。这时虚拟机可能就会启动后备预案,临时启用Serial Old收集老年代,这样停顿时间很长。

由于CMS并发清理时,用户线程还在运行,伴随产生新的垃圾,而这一部分垃圾出现在标记之后,只能下次GC时再清理。这一部分垃圾就称为”浮动垃圾“。

  • promotion failed: 在进行Minor GC时,Survivor空间不足,对象只能放入老年代,而此时老年代也放不下造成的,多数是由于老年代有足够的空闲空间,但是由于碎片较多,新生代要转移到老年带的对象比较大,找不到一段连续区域存放这个对象导致的
    CMS收集器对CPU资源非常敏感: 默认启动的回收线程数是(CPU+3)/4. 当CPU 4个以上时,并发回收垃圾收集线程不少于25%的CPU资源。
    解决办法:

  • 垃圾碎片的问题: 针对这个问题,这时候我们需要用到这个参数: -XX:CMSFullGCsBeforeCompaction=n 意思是说在上一次CMS并发GC执行过后,到底还要再执行多少次 full GC才会做压缩。默认是0,也就是在默认配置下每次CMS GC顶不住了而要转入full GC的时候都会做压缩,开启内存碎片合并整理,内存整理过程是无法并发的,空间碎片问题没了,但停顿时间变长。

  • concurrent mode failure: 只需要设置两个参数即可

-XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=60:是指设定CMS在对内存占用率达到60%的时候开始GC。G1中也有个类似的。
由于在垃圾收集阶段用户线程还需要运行,那也就还需要预留有足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集。
当然也不能设置过高,比如90%,这时候虽然GC次数少,但是,却会导致用于用户线程空间小,效率不高,太低10%,你自己想想会怎么样,体会体会!

  • remark阶段停顿时间会很长的问题: 解决这个问题巨简单,加入 -XX:+CMSScavengeBeforeRemark。在执行remark操作之前先做一次 Young GC,目的在于减少年轻代对老年代的无效引用,降低remark时的开销

CMS什么时候FUll GC

  1. 旧生代空间不足

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

  1. Permanet Generation空间满

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

  1. CMS GC时出现promotion failed和concurrent mode failure
  2. 统计得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间

Hotspot为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象,在进行Minor GC时,做了一个判断,如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间,那么就直接触发Full GC。

G1这个另类下一篇再说,先看看上面这几种收集器的组合使用:

收集器组合

收集器大致可以按这张图进行组合使用:
在这里插入图片描述
接下来,直接来一张图,简单明了。
在这里插入图片描述

本文参考https://blog.ouyangsihai.cn/shen-ru-li-jie-java-xu-ni-ji-cms-la-ji-hui-shou-qi.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/64689.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!