JVM之不同垃圾收集器比较


涉及这一部分的东西都是晦涩难懂的,加上平时开发中很少有这方面技能要求,在很长的一段时间我都是懵逼的,但是面试时又是一个重点,所以抽出时间来专门整理一篇这样的文章帮助本人理解学习。

Serial收集器
Serial收集器作为最基础,历史最悠久的收集器,曾经是JDK1.3之前HotSpot虚拟机新生代收集器的唯一选择。
算法:标记-复制
缺点:他是一个单线程收集器,在执行垃圾收集时会出现"Stop The World",所有的用户线程暂停,即系统出现卡顿。
优点:因为它是单线程执行,在单核或者核数比较少的处理器环境中它没有线程切换的开销,专心做垃圾回收,可以获得最高的手机效率。
适用场景:桌面应用和近几年流行起来的部分微服务中,分配给虚拟机管理的内存非常小,收集一两百兆的新生代只需要几十毫秒,最多一百毫秒,
		只要是频率不高,用户可以说是无感知,这类场景下还是一个非常好的选择。
ParNew收集器
ParNew是Serial的多线程版本,除了是多线程并行收集之外并没有其他的创新,但是在JDK7之前,它是除了Serial唯一一个可以和CMS收集器配合工作的
收集器(关于CMS收集器后面会说,为什么要配合使用,HotSpot虚拟机是基于分代思想设计的,Serial和ParNew是新生代收集器,必须配合老年代收集器
使用)。在JDK9,官方不再推荐ParNew+Serial Old(Serial的老年代收集器),所以ParNew只能和CMS配合使用,可以理解为ParNew并入和CMS,从此
ParNew退出了历史舞台。单核系统中,ParNew并不会展示出比Serial更好的效果,在多核处理器中,默认开启的线程数和处理器核数相同,可以使用
-XX:ParallerGCThread参数限制垃圾回收线程数。
算法:标记-复制
Parallel Scavenge收集器
也是一款新生代收集器,同样使用标记-复制算法实现,它和ParNew非常相似,其他收集器关注点是尽可能地缩短垃圾收集时的用户线程停顿时间,但是
Paraller Scavenge目标时达到一个可控制的吞吐量。吞吐量就是处理器用于运行用户代码的时间和处理器总耗时的比值。

吞吐量= 运 行 用 户 代 码 时 间 运 行 用 户 代 码 时 间 + 运 行 垃 圾 收 集 时 间 \frac{运行用户代码时间}{运行用户代码时间+运行垃圾收集时间} +

两个参数可以控制吞吐量,-XX:MaxGCPauseMillis(控制垃圾收集停顿时间),-XX:GCTimeRatio(代码运行时间/垃圾收集时间)。
 -XX:MaxGCPauseMillis参数允许一个大于0的毫秒数,这个参数设置越小,垃圾收集的频率就会越高,我们想象一下300M的新生代肯定比500M的收集更
 快,但是垃圾量达到300M的频率也会更高,触发垃圾回收的频率也会更高,原来10秒收集一次,卡顿100毫秒,现在5秒收集一次,卡顿70毫秒,停顿时间
 下降了,但是吞吐量下降了。
 -XX:GCTimeRatio参数允许的值0-100的整数,我们想要知道垃圾收集时间占总时间的比值,假如这个值设置为19,我们可以根据吞吐量的公式得出吞吐
 量为95%,反之垃圾收集所占总时间比5%。默认是99,即垃圾收集所占总时间比为1%。

-XX:GCTimeRatio有人可能会问,既然可以达到最大的吞吐量,我为什么要调小呢?我们回到上面的问题,500M和300M相比,吞吐量大的卡顿时间边长,反之卡顿时间变短,这里可能就是这个参数的意义

优点:吞吐量可控,多线程并发收集,效率高
缺点:JDK6之前只能配合Serial Old使用,直到JDK6开始Parallel Old的出现,可以搭配Parallel Old使用。
Serial Old 收集器
Serial Old是Serial收集器的老年代版本,单线程运行,基于标记整理算法,这个收集器的主要意义也是提供客户端模式下的HotSpot虚拟机使用。
Parallel Old收集器
Parallel Old是Parallel ScaVenge收集器的老年代版本,支持多线程并发收集,基于标记-整理算法实现。
适用场景:注重吞吐量或者处理器资源比较稀缺的场合
CMS收集器
它的出现是为了实现以最短回收停顿时间这一目标,在互联网网站或者基于浏览器的B/S架构中系统的响应速度是最为关注的,系统的卡顿会给用户带来非常
不好的体验,我们公司就是用的CMS收集器。
它是基于标记-清除算法实现的,它的收集过程分为四个阶段
 1. 初始标记:会出现Stop The World,只是标记能被GC Roots直接关联到的对象,速度很快
 2. 并发标记:与户线程并发执行,从GC Roots直接关联的对象,也就是初始标记的对象开始遍历整个对象图,耗时比较长,但是不会出现
 	Stop The World
 3. 重新标记:为了解决并发标记阶段因为用户线程继续运行导致的对象引用关系变化的部分(关于并发标记有一个增量更新和原始快照更新的区别,
    此处用的就是增量更新方法),该阶段会出现Stop The World,但是时间也是非常短
 4. 并发清除:清理掉通过前面三个标记阶段标记为已经死亡的对象,此阶段不会出现Stop The World,与用户线程并发执行

CMS垃圾收集器虽然并发环境下停顿短,但是因为他会占用一部分处理器资源,导致应用程序的资源占用量变小,吞吐量降低。
CMS默认开启线程数(处理器核心数+3)/4,即在四核及以上处理器中,垃圾收集占用资源不超过25%,核数越多,所占比例越小。
因为CMS并且因为是并发标记,在标记阶段用户线程还在执行,会出现新的垃圾,本次无法收集,只有下次再收集。它是基于标记清除算法,会让内存碎片化
当一个大对象要创建时找不到对应大小的空间,就会触发Full GC,JVM中提供了碎片整理的参数,
-XX:CompactAtFullCollection和-XX:CMSFulGCsBeforeCompaction,都是用于在Full出发之前进行一次碎片整理。
Garbage First-G1
G1收集器在JDK9取代了Parallel Sacvenge加Parallel Old的组合,CMS也被声明为不推荐使用,在G1出现之前其他的收集器收集的范围要么是整
个新生代,要么是整个老年代,要么是整个Java堆。而G1把Java堆划分为多个大小相同的独立的区域(Region),每个Region根据需要划分为新生代
的Eden空间,Suvivor空间或者老年代空间。Region中还有一类特殊的区域(Humongous),专门用于存储大对象,G1认为只要是大小超过Region一半
的对象就是大对象。
G1这种设计思路同时产生了几个问题,
1. 如何解决跨代引用,(记忆集)
2. 并发标记阶段如何保证垃圾收集线程和用户线程互不影响,使用原始快照,CMS是用的增量更新
3. 垃圾收集的停顿时间如何设置

收集过程
a. 初始标记:标记GC Roots能够直接关联到的对象,修改TAMS指针值,让下一阶段用户线程并发运行时,能够正确的在可用的Region中分配对象,
	会出现Stop The World
b. 并发a标记:从GC Roots开始,用可达性分析算法扫描整个堆中的对象图,找出需要回收的对象,与用户线程并发执行,扫描完对象图之后,还需要
	重新处理SATB并发时有引用变动的对象
c. 最终标记:Stop The World,非常短暂,用于处理并发标记阶段结束后仍然遗留下来的SATB记录
d. 筛选回收:这一阶段会更新Region的统计结果,对各个Region的回收价值和成本进行排序,然后根据用户期望的停顿时间指定回收计划,可以选择多
	个Region构成回收集,把决定回收的那一部分的Region中存活的对象复制到空的Region,然后清理掉旧的Region,这部操作涉及到对象的移动,所
	以会停止用户线程,出现Stop The World
	
优点:可以指定最大停顿时间、Region的内存布局,按收益动态制定垃圾收集策略,整体看是标记-整理,但是局部又是标记-复制,两个维度看都不会产
	生内存碎片,有利于程序长时间运行。
缺点:G1为了解决跨代引用的问题,有更多的卡表,并且卡表的设计比CMS更加复杂,所以相对于CMS占用更多的内存资源。

垃圾收集器的好坏主要从,内存占用,吞吐量和Stop The World的时间三要素去衡量,能达到两项就是一款很好的收集器。
以上内容来自于《深入理解Java虚拟机》这本书,本人正在阅读,收获颇多,记录一篇文档当作学习笔记。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值