垃圾回收器
垃圾算法是内存回收的方法论,则垃圾回收期就是内存回收的具体表现。
Hotspot JVM1.6的垃圾收集器
如图所示,如果两个收集器见存在连线,就说明他们可以搭配使用。没有最好的收集器,只有最合适的收集器。
1.Serial收集器
Serial收集器是最基本,历史最悠久的收集器,是JDK1.3之前虚拟机新生代收集的唯一选择。单线程收集器,不仅仅只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是他在进行垃圾收集时,必须暂停其他所有的工作线程(Stop The World,后台自动发起和自动完成的。在用户看不见的情况下把用户的正常工作的线程全部停掉),直到他收集结束。
优点:是虚拟机运行在Client模式下的默认新生代收集器,简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾回收可以获得最高的单线程收集效率。使用作新生代的收集器的时候,停顿时间控制在几十毫秒最多一百毫秒,只要不是频繁发生,这点停顿完全可以接受,所以对Serial收集器对于Client下的虚拟机是很好的选择。
2.ParNew收集器
ParNew收集器其实就是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数(列如:-XX:SurvivorRatio、-XX:PretenureSizeThreshold、-XX:HandlePromotionFailure等)、收集算法、stop the world、对象而分配规则、回收策略等与Serial收集器一致,实际上这两种收集器也公用了相当多的代码。
ParNew/Serial Old收集器运行示意图
Serial收集器是Server模式下的虚拟机中首选的新生代收集器,因为除了Serial外,只有他能与CMS收集器配合工作。-XX:+UseConcMarkSweepGC选项后的默认新生代选择器,也可以使用-XX:UseParNewGC选项来强制指定它。
单CPU下不如Serial,多线程下也不一定比他强(线程交互的开销)。随着CPU数量的增加,他对于GC时系统资源的利用还是有好处的,默认开启的收集器线程数与CPU的数量相同,在CPU非常多的情况下,可以使用-XX:ParallelGCThreads参数来限制收集的线程数。
并行:多条垃圾收集线程同时工作,用户线程处于等待状态。
并发:指用户线程与垃圾收集线程同时执行(但不一定时并行的,可能会交替执行),用户程序继续运行,而垃圾收集程序运行在另一个CPU上。
3.Parallel Old收集器
时Parallel Scavenge收集器的老年代版本,使用多线程 和“标记-整理”算法。这个收集器在JDK1.6中开始提供。新生代使用了Parallel Scavenge 收集器,老年代只要能用Serial Old (单线程)收集器。 导致达不到整体应用上获得吞吐最大化 的效果。
在注重吞吐量优先和CPU资源敏感的场合,都可以有限考虑Parallel Scavenge加上Parallel Old收集器。
ParNew/Serial Old 收集器运行示意图
4.Parallel Scavenge收集器
新生代收集器,采用复制算法,并行的多线程收集器。主要是尽可能的缩短垃圾回收时用户线程的停顿时间,而Parallel Scavenge收集器的目的是达到一个可控制的吞吐量。
吞吐量指得是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即
吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),虚拟机一共运行了100分钟,执行垃圾收集花掉一分钟,吞吐量就是99%。
停顿时间越短越适合需要与用户交互的程序,良好的响应速度能提升用户的体验;而高吞吐量则可以最高效率地利用CPU时间,尽快地完成程序地运算任务,主要适合再后台运算不需要太多交互地任务。
控制吞吐量地两个参数:
控制最大垃圾收集停顿时间:-XX:MaxGCPauseMillis,值为大于0地毫秒值,收集器将尽力保证回收花费地时间不超过设定值。不建议太小。
GC停顿时间缩短是以牺牲吞吐量和新生代空间来换取的:系统把新生代调小,收集300M比500M快,但是收集频率会变高。原来10s收集一次,停顿100ms,现在5s一次,停顿7ms。
设置吞吐量大小:-XX:GCTimeRatio参数,大于0小于100的整数,默认值为99,允许最大1%的垃圾回收时间。
开关参数:-XX:UseAdaptiveSizePolicy,打开之后就不需要手动指定新生代的大小(-Xmn)、Eden和Survivor区的比例(-XX:SurvivorRatio)、晋升到老年代对象指令年龄(-XX:PretenureSizeThreshold)等细节参数,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间湖泊最大吞吐量,这种调节方式称为GC自适应调节策略。
5.CMS收集器
Concurrent Mark Sweep,以获取最短停顿时间为目标的收集器,标记-清除算法。
初始标记(CMS initial mark)
并发标记(CMS concurrent mark)
重新标记(CMS remark)
并发清除(CMS concurrent sweep)
初始标记、重新标记这两个步骤仍然需要“Stop the world ”,初始标记仅仅只是标记一下GC Roots Tracing 的过程,速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的莪拿一部分对象的标记记录,这个解读那的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间端。
整个过程中消耗最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,所以总体上来CMS收集器的内存回收过程是与用户线程一起并发执行的。
Concurrent Mark Sweep 收集器运行示意图
优点:并发收集,低停顿。
缺点:1.CMS收集器对CPU资源非常敏感。会降低吞吐量,导致用户程序的执行素的忽然降低,体验差。i-CMS(incremental Concurrent Mark Sweep)增量式并发收集器,再并发标记和并发清理的时候让GC线程、用户线程交替运行,尽量减少GC线程的独占资源的时间,这样整个垃圾收集的过程会更长,但对用户程序的影响会显得少一些,速度下降也没有那么明显。目前不倡导使用。
2.无法处理浮动垃圾(Floating Garage),可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。由于CMS并发清理阶段用户线程用户线程还在运行着,伴随着程序的运行自然还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在本次收集中处理掉他们,只好留待下一次GC时再将其清理,这一部分就称为“浮动垃圾”。
垃圾收集阶段用户线程还需要运行,即还需要预留足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全别填满了再进行收集,需要预留一部分空间提供并发收集时的程序运作使用。 默认为老年代空间的68%被激活,可以适当调高。—XX:CMSIntiatingOccupancyFraction的值来提高触发百分比,以便降低内存回收次数以获取更好的性能。设置太高会容易导致大量“Concurrent Mode Failure”失败,性能降低。
3.标记清除算法,产生大量空间碎片。空间碎片过多会给大对象分配带来麻烦,老年代空间足够,但是无法找到连续的足够大的内存空间,导致Full GC。
6.G1收集器
G1(Garbage First)基于标记-整理算法,不会产生空间碎片,可以非常精确的控制停顿。
可以实现不牺牲吞吐量的前提下完成低停顿的内存回收,它能极力地避免全区域的垃圾收集,之前的收集器进行收集的范围都是整个新生代或老年代,而G1将整个Java堆(包括新生代,老年代)划分为多个大小固定的独立区域,并且跟踪这些区域里面的垃圾堆积程度。在后台维护一个优先列表,每次根据允许的收集时间,优先回收垃圾里面最多的区域。区域划分及有优先级的区域回收,保证了G1收集器在有限的时间内可以获得最高的收集效率。



