目录
虚拟机学习系列 - 1 - 运行时数据区域
虚拟机学习系列 - 2 - 垃圾收集概述
虚拟机学习系列 - 3 - 垃圾收集算法
虚拟机学习系列 - 4 - 垃圾收集器
虚拟机学习系列 - 5 - 内存分配与回收策略
虚拟机学习系列均为读书笔记,《深入理解java虚拟机》 - 周志明
由于是初学,学习和记录的可能很不全面,也会由于工作内容的关系,只着重记录一些离我工作较近的内容,望广大读者见谅
和书中一样,笔记记录Sun的Hot Spot中的垃圾收集器
主要有以下几种
(图片转字:https://blogs.oracle.com/jonthecollector/entry/our_collectors)
相连的两个收集器是可以搭配使用的,至于?是什么,后面再说
还有一个问题,CMS和Serial Old都是老年代收集器,为什么他们可以搭配使用
在讲收集器之前,我想先明确两个概念,我觉得这样对之后的阅读更清晰
在本篇博客上下文中下面两个词语分别指
并行(Parallel):多条垃圾线程并行工作,但是此时用户线程仍然处于等待状态
并发(Concurrent):只用户线程与垃圾收集线程同时执行(并不一定是并行的,可能会交替执行),用户程序继续运行,而垃圾收集程序运行于另一个CPU上
由于工作内容的关系,所以还是以笔记的形式为主,并不打算过多的学习与阐述
文字和图片相互补充
1.Serial
2.ParNew
3.Parallel Scavenge
4.Serial Old
5.Parallel Old
6.CMS
7.G1
下面按顺序介绍
1.Serial收集器
这是一款最基本最古老的收集器,单线程,采取复制算法,并且在它干活的时候它会Stop The World ,类似土匪强盗,它来了你就得让路,必须要等到它干完活,然后你才能继续干活。不过貌似也有些道理,这边清理垃圾,你那边却在制造垃圾,这不是捣乱呢吗
但是对于用户来说,这是不可忍受的,虚拟机在后台自动发起和完成,用户只能傻等还不知道为什么在等,等的是谁,horrible!
下面再来看看它的优点,简单高效(与其他收集器的单线程比),限定单个CPU的时候,不会有线程交互的开销。
它是运行在client模式下默认的新生代收集器,如果给虚拟机管理的内存不大的情况下,收集几十甚至一二百mb的内存大概在几十至一百多毫秒内,只要不频繁收集,用户应该可以忍受,基本察觉不出来吧
2.ParNew收集器
Serial收集器的多线程版本,其余几乎和Serial一样,采取复制算法,并且在它干活的时候它会Stop The World
它是运行在server模式下默认的新生代收集器,可与CMS配合使用
ParNew在单CPU下不会比Serial效果好,甚至更差
它默认开启的收集线程数同CPU数量相同
3.Parallel Scavenge收集器
先明确个概念
吞吐量(Throughput) = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)
此收集器的目标是达到一个可控制的吞吐量,上面是个简单的数学公式,大家都知道,当运行用户代码时间/垃圾收集时间 的值越大,吞吐量就越接近1,
所以它也被称为吞吐量优先的收集器 ,采取复制算法,并行的多线程收集器。
Parallel Scavenge提供了两个参数 --XX:MaxGCPauseMillis(最大垃圾收集停顿时间)和--XX:GCTimeRatio(吞吐量大小)
MaxGCPauseMillis不是万能的,设置的太小,就要牺牲新生代空间和吞吐量了。收集100MB总比收集200MB要快的吧
,10秒收集一次每次100ms,变成5秒一次,每次70ms,看上去是不是更糟了
GCTimeRatio相当于吞吐量 的倒数
吞吐量大小
4.Serial Old收集器
单线程,采取标记-整理算法 ,并且在它干活的时候它会Stop The World ,主要在clinet下使用
另两大用途:
在jdk1.5及其以前版本中与Parallel Scavenge搭配(由于工作中已经告别了1.5之前的版本,甚至1.5也几乎告别了,所以我所有blog内容除了特殊强调外,均不包括1.5以前的jdk)
作为CMS后备方案
5.Parallel Old收集器
多线程,采取标记-整理算法,1.6中才开始提供
在注重吞吐量和CPU敏感的场合,都可以优先考虑Parallel Scavenge + Parallel Old
6.CMS(Concurrent Mark Sweep)收集器
以获取最短回收停顿时间为目标的收集器
运作过程:
初始标记(CMS initial mark)
并发标记(CMS concurrent mark)
重新标记(CMS remark)
并发清除(CMS concurrent sweep)
初始标记和并发标记依然Stop The World ,初始标记很快,只是标记下GC Roots能直接关联到的对象
并发标记是进行GC Roots Tracing的过程
重新标记是修正并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录(这个阶段比初始标记时间稍长,远比并发标记时间短)
耗时最长的为并发标记和并发清除,收集器线程可以与用户线程一起工作,人家这边打扫,你可以在那边捣乱了
优点显而易见
下面看看缺点:
1.对CPU资源非常敏感,当CPU数量不到4个的时候,CMS可能对用户程序影响很大。
CPU负载本来就很大的时候,还有分出一半的运算能力去执行收集器线程,这也很让人受不了。为了解决这种情况,虚拟机提供一种增量式并发收集器(Incremental Concurrent Mark Sweep/i-CMS)
使用抢占式来模拟多任务机制思想一样,就是在并发标记和并发清理的时候让GC线程和用户线程交替运行,减少GC线程独占资源的时间,不过现在i-CMS已经不再提倡使用,声明为deprecated
2.CMS收集器无法处理浮动垃圾(Floating Garbage),可能出现Concurrent Mode Failure而导致另一次Full GC
浮动垃圾:由于CMS并发清理阶段用户线程还会产生新的垃圾,这一部分垃圾出现在标记之后,所以只能留待下一次GC时候清理
CMS基于标记-清除算法,所以会产生大量空间碎片,为此它提供两个参数用来缓解这个问题
a.-XX:+UseCMSCompactAtFullCollection
Full GC后进行碎片整理,内存整理过程无法并发
b.-XX:CMSFullGCsBeforeCompaction
执行多少次不压缩的Full GC后跟着来一次带压缩的
7.G1收集器
上面的图片中的“?”在那篇blog是这样说的
That box represents the new garbage collector that we're currently developing called Garbage First or G1 for short
日期为By jonthecollector on Feb 01, 2008
由于书中没有详细介绍,对我工作暂时也没有什么太大的用处,所以我也就简略的总结一下:
G1基于标记-整理,不会产生空间碎片
可以非常精确的控制停顿时间
G1可以实现在基本上不牺牲吞吐量的前提下完成低停顿的内存回收,G1将整个java堆划分为多个大小固定的区域,并跟踪里面垃圾堆积程度,在后台维护一个优先级列表。每次根据运行的手机时间优先回收垃圾最多的区域(Garbage First)
这节对我来说记住这些就可以了,对于大家可能需要更深入的了解,请参考其他资料
笔记截图如下:
转贴请保留以下链接
本人blog地址