浅谈Java虚拟机的垃圾收集器

      最近一直在读周志明老师的《深入理解Java虚拟机》,刚好读到虚拟机的收集器,在这里堆各类收集器浅谈一下自己的理解。

Java虚拟机的收集器按照在虚拟机的heap分代可划分为两种类型:新生代收集器(young generation GC)和老年代收集器(tenured generation GC)。还有最新的G1收集器,可独立管理整个GC堆。

按照收集算法又可分为三类:标记-清除(Mark-Sweep)算法GC、复制(Copying)算法GC、标记-整理(Mark-Compact)算法GC。一般根据对象存活率以及执行效率分别为新生代和老年代设置不同的GC。按照惯例,在虚拟机堆新生代中,每次垃圾收集时都发现有大量对象死去,只有少量对象存活,选用复制(Copying)算法只要付出少量存活对象的复制成本就可以达到垃圾回收的目的;而在虚拟机堆老年代中,因为对象存活率高、没有额外内存空间为其分配担保,就必须选择使用标记-清除(Mark-Sweep)算法或标记-整理(Mark-Compact)算法进行垃圾收集。

1. 新生代收集器--------- 

  • Serial GC

      Serial收集器是最基本、发展历史最悠久的收集器,在JDK 1.3.1之前是虚拟机新生代收集的唯一选择。它的特点是单线程,对此最著名的解释就是“stop the world”。它只会使用一个CPU或一条收集线程去完成垃圾收集工作,而且在它进行垃圾收集时,必须暂停其他所有的工作线程直到它收集结束。虽然随着收集器的市场发展,但Serial GC仍然未被淘汰,而是作为了虚拟机运行在Client模式下的默认新生代收集器。因为桌面环境中分配给虚拟机的内存一般不会很大,对于Serial GC收集几十兆甚至一两百兆的新生代,停顿时间完全可以控制在几十毫秒最多一百毫秒以内,而且Java虚拟机的safe point 和safe region的存在不会造成收集器的频繁发生,所以Serial GC对运行在Client模式下的虚拟机是一个很好的选择。

  • ParNew GC

       ParNew GC其实就是Serial GC的多线程版本,除了使用多条线程进行垃圾收集之外,其余行为包括Serial GC可用的所有控制参数、收集算法(新生代复制算法、老年代标记-整理算法)、stop the world、对象分配规则、回收策略都与Serial GC完全一样。甚至在实现上,这两种GC也共用了相当多的代码。

        还有一点,ParNew GC作为许多运行在Server模式下的虚拟机新生代首选默认收集器,是因为除了Serial GC之外,只有它能够于老年代的收集器CMS(Concurrent Mark-Sweep,使用并发标记-清除算法的GC)配合。
  • Parallel Scavenge GC

Parallel Scavenge GC是一个新生代的并行多线程GC,也是使用了复制(Copying)算法进行垃圾收集,它与ParNew GC的区别就是专注点不一样:Parallel Scavenge GC专注于可控制的吞吐量(Throughput,=运行用户代码时间/(运行用户代码时间+垃圾收集时间)),虚拟机总共运行了100分钟,垃圾回收花了1分钟,那么吞吐量就是99%;而ParNewGC等收集器关注的是垃圾回收时间也就是用户线程停顿时间。

停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能够提升用户的体验。而高吞吐量可以高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算不需要太多交互的任务。

2. 老年代收集器--------- 

  • Serial Old GC

Serial Old是Serial GC的老年代版本,它同样是单线程的收集器,使用标记-整理(Mark-Compact)算法。主要用途在于给Client模式下的虚拟机使用。在Server模式下还有两大用途:1、在JDK 1.5以及之前的版本中与新生代的Parallel Scavenge GC搭配使用;2、作为老年代的CMS GC后备预案,即在其发生Concurrent Mode Failure时使用。

  • Parallel Old GC

Parallel Old GC是Parallel Scavenge GC的老年代版本,使用多线程和标记-整理(Mark-Compact)算法。这个GC是在JDK 1.6中才开始提供的,在此之前,如果新生代如果选择了Parallel Scavenge GC,老年代除了Serial Old GC之外别无他选,因为Parallel Scavenge GC无法与CMS配合工作。由于老年代Serial Old GC在服务器端应用性能上的拖累,就算新生代使用了Parallel Scavenge GC也未必会在整体应用上获得吞吐量最大化的效果。由于单线程的老年代GC无法充分利用服务器多CPU处理能力,在老年代很大而且硬件比较高级的环境中,这种组合的吞吐量甚至不一定有ParNew+CMS组合给力。

所以Parallel Old GC的出现,吞吐量优先的GC终于有了比较名副其实的应用组合,在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge GC+Parallel Old GC。

  • CMS GC

CMS(Concurrent Mark-Sweep)GC是一种以获取最短回收停顿时间为目标的GC。目前很大一部分的Java应用集中在互联网站或者B/S系统的服务器端,这类应用尤其重视服务的响应速度,希望停顿时间最短以求带给用户比较好的体验。CMS GC就是非常符合这类应用的需求。

CMS是一款优秀的GC,它的主要优点在名字上就已经体现出来了,并发收集、低停顿。但CMS还远达不到完美的成都,有以下3个明显缺点:

1、CMS GC对CPU资源非常敏感。在并发阶段,虽然不会导致用户线程停顿,但是因为占用了一部分线程(或者说CPU资源)而导致应用程序变慢,总吞吐量会降低,尤其是在CPU总体资源比较少的情况下。CMS默认启动的回收线程时(CPU数量+3)/4,当CPU在4个以上时并发回收垃圾的收集线程不少于25%的CPU资源,但是当2个CPU时,CMS对用户程序的影响就可能变得很大,如果本来CPU负载就比较大,还分出一半的运算能力去执行收集线程,就可能导致用户程序的执行速度忽然降低了50%。

2、CMS GC无法处理浮动垃圾(Floating Garbage)。由于CMS并发清理阶段用户线程还在运行着,伴随着程序运行自然就会有的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好留在下次GC时再清理掉。这一部分垃圾就称为“浮动垃圾”。如果CMS运行期间预留的内存无法满足程序需要,就会出现一次“Concurrent Mode Failure”失败,这是虚拟机将启动后备预案:临时启用Serial Old GC来重新进行老年代的垃圾收集,这样就停顿时间太长了。

3、CMS是一款基于标记-清除(Mark-Sweep)算法实现的GC,在收集结束时会有大量空间碎片产生。空间碎片过多时,将会给大对象分配带来很大麻烦,往往会出现老年代还有很大空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。不过,CMS GC提供了参数配置进行优化。

关于并行并发的名词解释:
并行(parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行在另一个CPU上。

3. 全代收集器--------- 

G1(Garbage-First) GC是当今GC技术发展的最前沿成果之一,从JDK 6u14中开始就有Early Access版本的G1 GC供开发人员实验、试用,由此开始G1 GC的试验状态持续数年时间,直至JDK 7u4,Sun公司才认为它达到足够成熟商用程度,移除了“Experimental”的标识。
G1是一款面向服务端应用的垃圾收集器。HotSpot团队赋予它的使命是(在比较长期的)未来可以替换掉JDK 1.5中发布的CMS GC。与其他 GC相比,G1具备如下特点:
并行与并发:G1能够充分利用多CPU、多核环境下的硬件环境,使用多个CPU来缩短Stop-The-World停顿的时间,部分其他GC原本需要停顿Java线程执行的GC动作,G1 GC仍然可以通过并发的方式让Java程序继续执行。
分代收集:虽然G1可以不需要其他GC配合就能独立管理整个GC堆,但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。
空间整合:与CMS的标记-清理(Mark-Sweep)算法不同,G1从整体来看是基于标记-整理(Mark-Compact)算法,从局部(两个Region)上来看是基于复制(Copying)算法实现的,这两种算法都意味着G1运行期间不会产生内存空间碎片,收集后能够提供规整的可用内存。
可预测的停顿:这是G1相对于CMS的另一大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾手机上的时间不得超过N毫秒,这几乎已经是实时Java(RTSJ)的垃圾收集器的特征了。

Java虚拟机的垃圾收集器常用参数

垃圾收集相关的常用参数
参数描述
UserSerialGC虚拟机运行在CLient模式下的默认值,打开此开关后,使用Serial+Serial Old的收集器组合进行内存回收
UserParNewGC打开此开关后,使用ParNew+Serial Old的收集器组合进行内存回收
UserConcMarkSweepGC打开此开关后,使用ParNew+CMS+Serial Old的收集器组合进行内存回收。Serial Old收集器将作为CMS收集器出现Concurrent Mode Failure失败后的后备收集器使用
UseParallelGC虚拟机运行在Server模式下的默认值,打开此开关后,使用Parallel Scavenge+Serial Old(PS MarkSweep)的收集器进行内存回收
UseParallelOldGC打开此开关后,使用Parallel Scavenge+parallel+Parallel Old的收集器组合进行内存回收
SurvivorRatio新生代中Eden区域与Survivor区域的容量比值,默认为8,代表Eden:Survivor=8:1
PretenureSizeThreshold直接晋升道老年代的对象大小,设置这个参数后,大于这个参数的对象将直接在老年代分配
MaxTenuringThreshold晋升到老年代的对象年龄。每个对象在坚持过一次Minor GC之后,年龄就增加1,当超过这个参数值时就进入老年代
UseAdaptiveSizePolicy动态调整Java堆中各个区域的大小以及进入老年代的年龄
HandlePromotionFailure是否允许分配担保失败,即老年代的剩余空间不足以应付新生代的整个Eden和Survivor区的所有对象都存活的极端情况
ParallelGCTHreads设置并行GC时进行内存回收的线程数
GCTImeRatioGC时间占总时间的比率,默认值为99,即允许1%的GC时间。仅在使用Parallel Scavenge收集器时生效
MaxGCPauseMillis设置GC的最大停顿时间。仅在使用Parallel Scavenge收集器时生效
CMSInitiatingOccupancyFraction设置CMS收集器在老年代空间被使用多少后触发垃圾收集。默认值为68%,仅在使用CMS收集器时生效
UseCMSCompactAtFullCollection设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片整理。仅在使用CMS收集器时生效
CMSFullGCsBeforeCompaction设置CMS收集器在进行若干次垃圾收集后再启动一次内存碎片整理。仅在使用CMS收集器时生效

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值