今天继续HotSpot的学习,聊聊垃圾收集器。
之前讲过垃圾收集算法,可以参考这里JVM系列(4)-垃圾收集算法,垃圾收集算法讲的是思想或者理论,垃圾收集器呢,就是其实现。
知识点1:HotSpot有哪些垃圾收集器呢?
参阅《深入理解Java虚拟机 JVM高级特性与最佳实践》,以JDK 1.7 Upgrade 14之后的HotSpot虚拟机为例。下图是HotSpot包含的垃圾收集器。
按官方的解释,Serial、ParNew/Parallel Scavenge属于新生代收集器;CMS、Serial Old(MSC)、Parallel Old是老年代收集器,G1即在老年代也在新生代。
为什么会有这么多收集器,有没有一个放之四海皆试用的垃圾收集器?
答案:没有,不同的垃圾收集器适用于不同的应用场景,没有万能的收集器。可以根据需求搭配不同的垃圾收集器,如上图,之间有连线的就是可以搭配使用的,没有连线的就是不能搭配使用。
下面逐一讲解各个垃圾收集器。
知识点2:Serial收集器
Serial收集器负责新生代的垃圾回收,采用的是复制回收算法。
这是一个单线程的收集器,也就是只使用一个收集线程来收集垃圾,而且更重要的是收集的时候,需要“Stop the world”,也就是暂停所有的执行线程,直到垃圾收集结束。
知识点2:ParNew收集器
这个没啥讲的,就是Serial收集器的多线程版本,还有一点,ParNew多用于Server模式,除了Serial收集器,它是唯一可以和CMS搭配使用的,CMS是重点,后面会讲到。
默认开启的线程数与CPU的个数相同。
单CPU情况下,比不过Serial收集器。
知识点3:Parallel Scavenge收集器
Parallel Scavenge和ParNew有很多相似之处。首先他们都负责收集新生代垃圾,其次,它们采用的收集算法都是“复制”,最后它们都是并行的多线程收集器。
Parallel Scavenge的关注点是吞吐量。
吞吐量 = 运行用户代码时间 / 运行用户代码时间 + 垃圾收集时间
参数-XX:UserAdaptiveSizePolicy可以开启自适应策略。
知识点4:Serial Old收集器
Serial Old是Serial收集器的老年代版本,它也是一个单线程收集器,但使用的是“标记-整理”回收算法。
这个收集器主要应用与Client模式的虚拟机,如果用在Server模式,主要有两个目的:一个是与Parallel Scavenge搭配使用,另一个是给CMS收集器做后备。
知识点5:Parallel Old收集器
Parallel Old是Parallel Scavenge的老年代版本,正如Serial Old是Serial收集器的老年代版本一样。在Parallel Old出现之前,Parallel Scavenge只能和Serial Old收集器搭配使用,由于Serial Old是单线程的,并且需要“Stop the world”,在性能上拖累了Parallel Scavenge收集器。Parallel Old出现之后,情况就不同了,在注重吞吐量和CPU资源敏感的场合,很适用Parallel Scavenge和Parallel Old的组合。
Parallel Old收集器采用的是标记-整理算法。
知识点6:CMS收集器
CMS收集器关注的点是“最短停顿时间”,我们知道,像Serial收集器,在垃圾收集的时候,需要停顿所有执行线程,那这个停顿的时间能不能尽可能的短呢?
CMS就是追求这个“最短停顿时间”的收集器。插播一句,像Parallel Scavenge收集器关注的则是“吞吐量”,高吞吐量则意味着高效利用了CPU。在注重交互的场景,客户更在意的是停顿时间,所以我们可以选择CMS收集器;在没有太多交互而存在大量计算的场景下,很适合选用Parallel Scavenge这类关注“吞吐量”的收集器。
CMS = Current Mark Sweep,从名字可以看出,CMS采用的是标记-清除算法。
运作过程如下图,主要有4个阶段:初始标记、并发标记、重新标记、并发清除。其中初始标记和重新标记,需要“Stop the world”,因为要标记哪些是垃圾,所以得停顿下啊,不能一边打扫,一边扔垃圾不是。而消耗时间较长的并发标记和并发清除则不需要停顿用户线程。
缺点:在并发标记和并发清除阶段,因为是和用户线程并发执行的,要消耗一部分资源,如果用户线程本身资源就很紧张,可能会导致应用程序执行变慢。
还有就是CMS收集器无法处理“浮动垃圾”。什么是“浮动垃圾”?我们知道在并发清除阶段,用户线程还是在执行的,也就是在并发清除的过程中,用户线程还是会产生垃圾的,这部分垃圾因为没有被标记,也就清除不了,这就是“浮动垃圾”。
最后是内存碎片,我们知道CMS采用的是标记-清除算法,这种算法本身就会产生内存碎片。可以通过一些参数,设置在什么情况下执行一下碎片整理。
知识点7:G1收集器
这个收集器很牛逼,像上面讲的收集器,都需要两两搭配使用,比如Serial收集器和Serial Old收集器,一个负责年轻代,一个负责老年代。而G1收集器单此一个挑大梁,同时负责新生代和老年代。不像前面讲的收集器,它们把堆空间会分为两个不同的连续区间(新生代区和老年区)。G1收集器则是把堆空间划分为多个不同的region,新生代region和老年代region也不是连续的。像下面这样。
G1会在后台维护一个列表,在允许的收集时间内,优先回收价值最大的region,也就是释放空间最多的region。
知识点7:GC日志关键字
采用的收集器不同,日志中记录的新生代名称也不同。
Serial : DefNew = Default New Generation
ParNew : ParNew = Parallel New Generation
Parallel Scavenge : PSYoungGen = Parallel Scavenge Young Generation
Tenured : 老年代
Perm : 持久代
知识点8:垃圾收集器参数总结
有个博客写的挺详细的,我就不做重复劳动了。
下面内容转自博客:http://blog.csdn.net/huxian1234/article/details/17163023
垃圾收集器参数总结
-XX:+<option> 启用选项
-XX:-<option> 不启用选项
-XX:<option>=<number>
-XX:<option>=<string>
参数描述
-XX:+UseSerialGC | Jvm运行在Client模式下的默认值,打开此开关后,使用Serial + Serial Old的收集器组合进行内存回收 |
-XX:+UseParNewGC | 打开此开关后,使用ParNew + Serial Old的收集器进行垃圾回收 |
-XX:+UseConcMarkSweepGC | 使用ParNew + CMS + Serial Old的收集器组合进行内存回收,Serial Old作为CMS出现“Concurrent Mode Failure”失败后的后备收集器使用。 |
-XX:+UseParallelGC | Jvm运行在Server模式下的默认值,打开此开关后,使用Parallel Scavenge + Serial Old的收集器组合进行回收 |
-XX:+UseParallelOldGC | 使用Parallel Scavenge + Parallel Old的收集器组合进行回收 |
-XX:SurvivorRatio | 新生代中Eden区域与Survivor区域的容量比值,默认为8,代表Eden:Subrvivor = 8:1 |
-XX:PretenureSizeThreshold | 直接晋升到老年代对象的大小,设置这个参数后,大于这个参数的对象将直接在老年代分配 |
-XX:MaxTenuringThreshold | 晋升到老年代的对象年龄,每次Minor GC之后,年龄就加1,当超过这个参数的值时进入老年代 |
-XX:UseAdaptiveSizePolicy | 动态调整java堆中各个区域的大小以及进入老年代的年龄 |
-XX:+HandlePromotionFailure | 是否允许新生代收集担保,进行一次minor gc后, 另一块Survivor空间不足时,将直接会在老年代中保留 |
-XX:ParallelGCThreads | 设置并行GC进行内存回收的线程数 |
-XX:GCTimeRatio | GC时间占总时间的比列,默认值为99,即允许1%的GC时间,仅在使用Parallel Scavenge 收集器时有效 |
-XX:MaxGCPauseMillis | 设置GC的最大停顿时间,在Parallel Scavenge 收集器下有效 |
-XX:CMSInitiatingOccupancyFraction | 设置CMS收集器在老年代空间被使用多少后出发垃圾收集,默认值为68%,仅在CMS收集器时有效,-XX:CMSInitiatingOccupancyFraction=70 |
-XX:+UseCMSCompactAtFullCollection | 由于CMS收集器会产生碎片,此参数设置在垃圾收集器后是否需要一次内存碎片整理过程,仅在CMS收集器时有效 |
-XX:+CMSFullGCBeforeCompaction | 设置CMS收集器在进行若干次垃圾收集后再进行一次内存碎片整理过程,通常与UseCMSCompactAtFullCollection参数一起使用 |
-XX:+UseFastAccessorMethods | 原始类型优化 |
-XX:+DisableExplicitGC | 是否关闭手动System.gc |
-XX:+CMSParallelRemarkEnabled | 降低标记停顿 |
-XX:LargePageSizeInBytes | 内存页的大小不可设置过大,会影响Perm的大小,-XX:LargePageSizeInBytes=128m |
Client、Server模式默认GC
新生代GC方式老年代和持久代GC方式
Client | Serial 串行GC | Serial Old 串行GC |
Server | Parallel Scavenge 并行回收GC | Parallel Old 并行GC |
Sun/oracle JDK GC组合方式
新生代GC方式老年代和持久代GC方式
-XX:+UseSerialGC | Serial 串行GC | Serial Old 串行GC |
-XX:+UseParallelGC | Parallel Scavenge 并行回收GC | Parallel Old 并行GC |
-XX:+UseConcMarkSweepGC | ParNew 并行GC | CMS 并发GC 当出现“Concurrent Mode Failure”时 采用Serial Old 串行GC |
-XX:+UseParNewGC | ParNew 并行GC | Serial Old 串行GC |
-XX:+UseParallelOldGC | Parallel Scavenge 并行回收GC | Parallel Old 并行GC |
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC | Serial 串行GC | CMS 并发GC 当出现“Concurrent Mode Failure”时 采用Serial Old 串行GC |
今天就到这儿,明天继续^_^。