上一章JVM系列02-垃圾回收算法中我们聊了垃圾回收的具体算法,本章我们一起来聊聊垃圾回收算法的具体应用实现垃圾回收器。不同的虚拟机厂商可以根据不同垃圾回收算法的组合实现自己的垃圾回收器各有不同,下面我们主要聊一聊Hotspot虚拟机实现的几种垃圾回收器。
如上图,这是目前为止Hotspot团队已经实现的几种不同垃圾回收器,可以看出有些垃圾回收器工作在新年代如serial、praNew,parallel Scavenge而有些工作在老年代,这正是符合Hotspot内存分代的基础,特殊的如G1横跨新年代和老年代,这是Hotspot开发的新一代垃圾回收器,希望由此代替之前不同分代使用不同垃圾回收器,但也不是说使用G1之后就不存在内存分代的概念,使用G1的时候同样也是有分代的概念只是和之前新年代、老年代的划分略有不同,这个我们在稍后G1章节中在详细聊聊。不同的垃圾回收器之间存在连线的就表示这两可以配合使用。
Serial
serial是Hotspot最早的垃圾回收器,是一款单线程的垃圾回收器,单线程意味这什么呢,意味着不仅仅是其只使用一个cpu去完成垃圾回收的事情,更重要的是在其回收垃圾期间将暂停所有用户线程,也就是所谓的stop the world,想想这是多么的可怕,你的应用每运行一段时间将暂停几分钟,这对于服务器应用无疑是无法接受的,我们说超过200ms的响应对于用户来说就是灾难,更不用说服务器应用肯定分配的堆内存大点,自然垃圾回收的时间长点,正因为如此serial收集器只能用在桌面级的应用中,因为桌面级单机应用分配的内存空间不会很多,交互也不是非常的频繁,对于应用几十分钟停顿毫秒级别还是可以接受的。
优点:当然也不是说serial收集器一无是处,因为其单线程所以避免了线程之间切换带来的消耗更加高效,具体serial收集器运行如下图:
parNew
parNew其实就是serial的多线程版本,也就是使用多条线程进行垃圾回收,其余使用的算法等基本和serial一致
parallel Scavenge
parallel Scavenge也是一个新生代垃圾回收器,但是其与其他垃圾器存在明显的区别,其他的垃圾回收器关注点在于如何缩短垃圾回收的时间,而parallel Scavenge更加关注系统的吞吐量,所以也被称为吞吐量有限垃圾回收器,何为吞吐量呢?吞吐量=运行用户代码的时间/(运行用户代码的时间+垃圾回收的时间),可能会有同学有疑问,垃圾回收的时间短了,分母小了自然吞吐量提升了呀,局部的看确实是这样的,但是你可以想想垃圾回收的时间缩短,无非是堆内存的空间减小,但是垃圾回收时间的减少无疑将引起垃圾回收频率的增加,比如之前500M内存,每10s收集一次,每次100ms,现在内存300M,每5s收集一次,每次70ms,从局部看好像是垃圾收集的时间缩短了,但是整体分析吞吐量并没有提升。parallel Scavenge提供了几个很重要的参数:
- -XX:MaxGCPauseMills 取值为大于0的毫秒,意思是尽可能的保证内存回收的时间不超过这个值,可千万不要以为既然这么优秀,但是不是设置的越小越好呢,有得必有失嘛,保证内存回收不超过固定的值肯定不是凭空得来的,原理就是我们前面说的内存的空间减小了,自然回收的时间少了,但是一定别忘了此时垃圾回收的频率自然会上升。
- -XX:GCTimeRatio 取值在0-100之间,表示垃圾回收占总时间的比例,也就是吞吐率的倒数
- -XX:UseAdaptiveSizePolicy 这是一个开关参数,只要打开这个参数,就不需要手动的指定新生代的大小,晋升老年达的对象大小等等,虚拟机会根据具体的监控信息自动的调整,这对于不是非常熟悉调优的同学无疑是一个巨大的福利。
serial old
serial old顾名思义就是serial的老年代版本,依旧是单线程收集,收集算法依然保持不变
parallel old
parallel old就是parallel scavenge的老年代版本,多线程标记-整理算法,在parallel old出现之前parallel scavenge其实一直处于一个尴尬的局面,因为新生代采用parallel scavenge收集器,老年代只能用serial old配合使用,其吞吐量优先的优势并没有得到发挥,直到parallel old收集器的出现才让parallel scavenge吞吐量优先名副其实。
CMS
CMS(Concurrent Mark Sweep)收集器是一款以最短停顿时间为目标的垃圾回收器,目前大量使用在服务器应用中,在交互频繁的应用中有较好的发挥,如其名其使用的是标记-清除算法,具体垃圾回收过程如下图分为以下4个步骤:
-
初始标记
-
并发标记
-
重新标记
-
并发清除
其中,初始标记和重新标记依然需要“stop the world”,但是初始标记仅仅只是标记一下GC Root能直接关联的对象,速度非常快。并发标记是真正的开始整个GC Root链路的标记,重新标记是处理在并发标记的过程中用户程序变化部分,这部分也需要“stop the world”,并发清除是正在的清除环节。从整体看标记阶段、清除阶段都可以也用户线程同时执行,也算是做到了并发。
当然CMS也不是非常的完美,也存在一下几个问题: -
cms对cpu资源非常敏感。其实服务器程序对于cpu资源都非常的敏感,而cms在并发标记和并发清除环节还要抢占cpu,导致用户线程的服务时间降低,cms并发线程默认为(cpu数+3)/4,可以看出cpu资源越多,其性能越好,cpu资源不足4个是至少需要占用25%的性能消耗还是比较可怕,所以还是建议cms使用在cpu资源充足的应用中。
-
无法处理浮动垃圾。 何为浮动垃圾呢,浮动垃圾是指在并发清除环节用户线程产生的垃圾,因为本次产生的垃圾没有标记,只能在下次回收。同时因为在清除垃圾的时候用户线程也在运行,所以需要预留一定的空间给用户线程不能等到整个老年代快填满的时候再去垃圾回收,但内存空间不足以使老年代回收使将产生“concurrent mode failure”错误。在jdk1.6中默认老年代占用阈值超过92%将会触发垃圾回收,当空间不足出现“concurrent mode failure”错误时虚拟机默认启动serial old作为老年代回收器,其停顿时间将变长许多。
-
产生内存碎片。 从名字 Mark sweep可以看出cms使用的是标记-清除算法,我们知道标记清除算法可能会产生大量的内存碎片,导致无法为大对象分配空间,从而不得不再次触发GC
G1
G1(Garbage First )是Hotspot最新的研究成果,目标是在未来替换掉CMS。G1的关注点也是尽可能的降低停顿时间,G1的回收过程大致如下图:
G1的整体回收过程与CMS类似,不同的是G1具体以下几个特点:
- 并发
G1在回收的过程中依然可以和用户线程并发的执行 - 空间整合
G1中依然存在分代的概念,但是G1将内存划分为多个regoin块,垃圾回收不在是扫描整个堆而是在region中进行 - 可预测的停顿时间
G1提供可以预测的停顿时间配置,如何做到呢?G1为每个regoin维护了一个Remembered Set其中记录中这个regoin中引用的对象是否在本regoin之外,从而避免扫描整个区域的regoin,同时G1会对Remembered Set进行回收价值的排序,即回收得到的空间和回收所需的时间,优先回收价值更高的regoin,也真是garbage first的体现。
以上是我们对于Hotspot虚拟机垃圾回收器的整理,每个垃圾回收器都有自己的回收区域,回收算法,相互之间配合完成垃圾回收的工作,相信的不远的未来G1技术的更加成熟,垃圾回收可以真正交给虚拟机。整理匆忙,难免出现笔误和理解的误区,还望各位读者批评指正。