最近恰好有时间,就简单的看了下JVM的几种垃圾回收器,它们都是计算机历史发展的产物,先简单的作一个整理,并无哪一款垃圾收集器就必定是最优,还须要结合使用场景、参数配置等进行考量,根据系统状况搭配出尽量合理优质的垃圾回收策略,而这每每须要经验的积累;html
若是说收集算法是内存回收的方法论,垃圾收集器就是内存回收的具体实现。java
下图展现了7种做用于不一样分代的收集器,若是两个收集器之间存在连线,就说明它们能够搭配使用。算法
并发与并行编程
这两个名词都是并发编程中的概念,在谈论垃圾收集器的上下文语境中,它们能够解释以下。服务器
并行(Parallel):指多条垃圾收集线程并行工做,但此时用户线程仍然处于等待状态。多线程
并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不必定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行于另外一个CPU上。并发
Serial收集器 [ˈsɪəriəl]性能
Serial 收集器是最基本、历史最悠久的收集器,曾经(在JDK 1.3.1以前)是虚拟机年轻代收集的惟一选择。优化
特性网站
年轻代,单线程收集器,但它的“单线程”的意义并不只仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工做,更重要的是在它进行垃圾收集时,必须暂停其余全部的工做线程(Stop the world),直到它收集结束。
优点
简单而高效(与其余收集器的单线程比),对于限定单个CPU的环境来讲,Serial收集器因为没有线程交互的开销,专心作垃圾收集天然能够得到最高的单线程收集效率。
应用场景
在用户的桌面应用场景中,分配给虚拟机管理的内存通常来讲不会很大,收集儿十兆甚至一两百兆的新生代(仅仅是新生代使用的内存,桌面应用基本上不会再大了),停顿时间彻底能够控制在几十毫秒最多一百多毫秒之内,只要不是频繁发生,这点停顿是能够接受的。因此,Serial 收集器对于运行在Client模式下的虚拟机来讲是一个很好的选择。
参数控制
-XX:+UseSerialGC -- 串行收集器
ParNew收集器
ParNew收集器其实就是Serial收集器的多线程版本;
特性
ParNew收集器其实就是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集以外,其他行为包括Serial收集器可用的全部控制参数、收集算法、Stop The World、对象分配规则、回收策略等都与Serial收集器彻底同样,在实现上,这两种收集器也共用了至关多的代码。
优点
随着可使用的CPU的数量的增长,它对于GC时系统资源的有效利用仍是颇有好处的。
它默认开启的收集线程数与CPU的数量相同,在CPU很是多(譬如32个,如今CPU动辄就4核加超线程,服务器超过32个逻辑CPU的状况愈来愈多了)的环境下,可使用-XX: ParaIlelGCThreads参数来限制垃圾收集的线程数。
劣势
ParNew收集器在单CPU的环境中绝对不会有比Serial收集器更好的效果,甚至因为存在线程交互的开销,该收集器在经过超线程技术实现的两个CPU的环境中都不能百分之百地保证能够超越Serial收集器。
应用场景
ParNew收集器是许多运行在Server模式下的虚拟机中首选的新生代收集器。
参数控制
-XX:+UseParNewGC -- ParNew 年轻代收集器
-XX:ParaIlelGCThreads -- 限制垃圾收集的线程数
备注
除了Serial收集器外,目前只有它能与CMS收集器配合工做。
Parallel Scavenge收集器 [ˈpærəlel] [ˈskævɪndʒ]
特性
相比其余收集器,Parallel收集器更关注系统的吞吐量。
吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)
优点
GC自适应的调节策略,当使用-XX:+UseAdaptiveSizePolicy参数后,就不须要手工指定新生代的大小、Eden与Survivor区的比例、晋升老年代对象年龄等细节参数了,只须要把基本的内存数据设置好(如-Xmx),而后使用MaxGCPauseMillis参数或GCTimeRatio参数给VM设立一个优化目标,虚拟机会根据当前系统的运行状况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量,这种调节方式称为GC自适应的调节策略(GC Ergonomics)。
应用场景
若是对于收集器运做原理不太了解,手工优化存在困难的时候,使用Parallel Scavenge收集器配合自适应调节策略,把内存管理的调优任务交给虚拟机去完成将是一个很不错的选择。
参数控制
-XX:+UseParallelGC -- 使用Parallel收集器 + 老年代串行
-XX:+UseAdaptiveSizePolicy -- GC自适应的调节策略
收集器提供了两个参数用于精确控制吞吐量:
-XX:MaxGCPauseMillis -- 最大垃圾收集停顿时间,是一个大于0的毫秒数,收集器将尽力保证内存回收花费的时间不超过设定值,可是GC停顿时间缩短是以牺牲吞吐量和新生代空间来换取的;
-XX:GCTimeRatio -- 直接设置吞吐量大小();默认值为99,就是容许最大1%的垃圾收集时间。
Serial Old收集器
Serial Old是Serial收集器的老年代版本。
特性
老年代,单线程收集器,使用“标记-整理”算法。
优点
应用场景
①Client模式
Serial Old收集器的主要意义也是在于给Client模式下的虚拟机使用。
②Server模式
若是在Server模式下,那么它主要还有两大用途:一种用途是在JDK 1.5以及以前的版本中与Parallel Scavenge收集器搭配使用,另外一种用途就是做为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。
参数控制
备注
Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本。
特性
老年代,多线程收集器,使用“标记—整理”算法,该收集器在JDK1.6中才开始提供。
优点
应用场景
在注重吞吐量以及CPU资源敏感的场合,均可以优先考虑Parallel Scavenge + Parallel Old收集器二者的组合。
参数控制
-XX:+UseParallelOldGC -- 使用Parallel收集器+ 老年代并行
CMS收集器
CMS(Concurrent Mark Sweep)收集器,是一种以获取最短回收停顿时间为目标的收集器。
特性
从名字(包含“Mark Sweep”)上就能够看出CMS收集器是基于“标记-清除”算法实现的,它的运做过程相对于前面几种收集器来讲要更复杂一些,整个过程分为4个步骤,包括:
①初始标记(CMS initial mark):仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,须要“Stop The World”。
②并发标记(CMS concurrent mark):经过根搜索算法(GC Roots Tracing)判断对象是否仍在使用中。
③从新标记(CMS remark):为了修正并发标记期间因用户程序继续运做而致使标记产生变更的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但远比并发标记的时间短,仍然须要“Stop The World”。
④并发清除(CMS concurrent sweep):清除标记对象。
优点
并发收集,低停顿。
缺点
①CMS收集器对CPU资源很是敏感:其实,面向并发设计的程序都对CPU资源比较敏感。在并发阶段,它虽然不会致使用户线程停顿,可是会由于占用了一部分线程(或者说CPU资源)而致使应用程序变慢,总吞吐量会下降。
②CMS收集器没法处理浮动垃圾:因为CMS并发清理阶段用户线程还在运行着,伴随程序运行天然就还会有新的垃圾不断产生,这一部分垃圾出如今标记过程以后,CMS没法在当次收集中处理掉它们,只好留待下一次GC时再清理掉,这一部分垃圾就称为“浮动垃圾”。
③CMS收集器会产生大量空间碎片:CMS是一款基于“标记—清除”算法实现的收集器,这意味着收集结束时会有大量空间碎片产生。空间碎片过多时,将会给大对象分配带来很大麻烦,每每会出现老年代还有很大空间剩余,可是没法找到足够大的连续空间来分配当前对象,不得不提早触发一次Full GC。为了解决这个问题,CMS收集器提供了两个开关参数-XX:+ UseCMSCompactAtFullCollection && -XX:+CMSFullGCsBeforeCompaction;
应用场景
目前很大一部分的Java应用集中在互联网站或者B/S系统的服务端上,这类应用尤为重视服务的响应速度,但愿系统停顿时间最短,以给用户带来较好的体验。CMS收集器就很是符合这类应用的需求。
参数控制
-XX:+UseConcMarkSweepGC -- 使用CMS收集器
-XX:+ UseCMSCompactAtFullCollection -- 用于在“享受”完FullGC以后,免费附赠一次碎片整理过程,因为内存整理的过程是没法并发的,因此时间会比较长;
-XX:+CMSFullGCsBeforeCompaction -- 用于执行完N次不压缩FullGC以后,跟着来一次带压缩的;
-XX:ParallelCMSThreads(后期又称为ConcGCThreads) -- 定义并发CMS过程运行时的线程数。若是该标志未设置,JVM会根据并行收集器中的-XX:ParallelGCThreads参数的值来计算出默认的并行CMS线程数。ParallelCMSThreads = (ParallelGCThreads + 3)/4,ParallelGCThreads默认会被设置为CPU的数量;
G1收集器
G1收集器是垃圾收集器理论进一步发展的产物,HotSpot开发团队赋予它的使命是将来能够替换掉JDK 1.5中发布的CMS收集器。
G1收集器打破了年轻代与老年代的物理界限。
取而代之的是,G1算法将堆划分为若干个区域(Region),它仍然属于分代收集器。以下图所示,堆被分为 Eden、Survivor、old generation区。此外,还有第四种类型的对象被称为巨无霸区域(Humongous regions),用来保存比标准块(standard region)大50%及以上的对象,它们存储在一组连续的区中,最后一个类型是堆内存中的未使用区(unused areas).
特性
基于“标记一整理”算法,可独立完成分代收集,打破年轻代与老年代的物理界限,可预测停顿;
优点
①不会产生内存碎片:基于“标记一整理”算法,也就是说它不会产生空间碎片,这对于长时间运行的应用系统来讲很是重要。同时也不会由于分配大对象时没法找到连续内存空间而提早触发下一次GC。
②可预测停顿:它能够很是精确地控制停顿,既能让使用者明确指定在一个长度为M毫秒的时间片断内,消耗在垃圾收集上花费的时间不得超过N毫秒,这几乎已是实时Java (RTSJ)的垃圾收集器的特征。
应用场景
能够像CMS收集器同样,GC操做与应用的线程一块儿并发执行;
须要可预测的GC暂停耗时;
参数控制
-XX:+UseG1GC -- 启用G1收集器
-XX:MaxGCPauseMillis -- 设置GC的最大暂停时间(单位:毫秒)
-XX:InitiatingHeapOccupancyPercent -- 启动并发GC时的堆内存占比,基于整个堆的使用率,而不仅是某一代内存的使用比例;默认值为 45;值为 0 则表示“一直执行GC循环“;
备注
①不要设置年轻代的大小(Young Generation Size),倘若经过 -Xmn 显式地指定了年轻代的大小, 则会干扰到 G1收集器的默认行为;
②设置 MaxGCPauseMillis 时不该该使用平均响应时间做为指标,而应该考虑使用目标时间的90%或者更大做为响应时间指标.。也就是说90%的用户请求响应时间不会超过预设的目标值,不过该暂停时间只是一个目标,并不能保证老是获得知足。
参考:
《深刻理解Java虚拟机:JVM高级特性与最佳实践_周志明》
java垃圾回收器:http://www.jianshu.com/p/50d5c88b272d
http://www.cnblogs.com/ityouknow/p/5614961.html