垃圾收集器

垃圾收集器

  前面的垃圾收集算法是理论,垃圾收集器则是具体的实现。下图是HotSpot里的收集器,中间的横线表示分代,有连线表示可以组合使用。虚拟机所处的区域表示它是属于新生代收集器还是老年代收集器。

Serial 收集器

  是一个单线程的收集器,只能使用一个CPU或一条线程去完成垃圾收集;在进行垃圾收集时,必须暂停所有其他工作线程,直到收集完成。Serial/Serial Old 收集器运行示意图:

  缺点:Stop-The-World。

  优势:简单。对于但CPU的情况,由于没有多线程交互开销,反而可以更高效。是Client模式下默认的新生代收集器。

ParNew 收集器

  是Serial收集器的多线程版本。在许多运行在Server模式下首选的新生代垃圾收集器(除了Serial收集器外,唯一能够和CMS收集器配合工作的收集器).ParNew收集器也是使用-XX:+UseConcMarkSweepGC选项后默认的新生代收集器,也可以使用-XX:+UseParNewGC选项来强制制定它。同时可以通过使用-XX:ParallelGCThreads参数来限制垃圾收集器的线程数.ParNew/Serial Old 收集器运行示意图:

垃圾收集语境下的并发与并行概念

  • 并行(Parallel):指多条垃圾收集线程并行工作,用户线程仍然处于等待状态。
  • 并发(Concurrent):用户线程与垃圾收集线程同时执行。

Parallel Scavenge 收集器

  新生代收集器,使用复制算法、并行的多线程收集器。Parallel Scavenge 收集器的目标是达到一个可控制的吞吐量(Throughput)。这里的吞吐量是指CPU用于运行用户代码的时间与CPU总消耗时间的比值。吞吐量=运行用户代码时间/(运行用户代码时间+GC时间)。主要适合在后台运算而不需要太多交互的任务。

  Parallel Scavenge提供了两个参数用户精确控制吞吐量。-XX:MaxGCPauseMillis控制最大垃圾收集停顿时间,收集器将尽量保证内存回收时间不超过设定值。把这个参数设置的设置的小降低GC停损时间缩短是以降低新生代空间为代价的,可能会造成垃圾收集的频率增加。-XX:GCTimeRatio直接设置吞吐量大小,即垃圾收集时间占总时间的比率(总时间/垃圾收集时间=GCTimeRatio)。

  Parallel Scavenge 收集器允许采用GC自适应的调节策略,也就是让虚拟机根据收集到的运行时数据自行决定各个分代的大小等与垃圾回收有关的配置,可以通过-XX:+UseAdaptiveSizePolicy。这种动态调整参数以提供合适的停顿时间或者最大吞吐量的方式呗成为GC自适应的调节策略。

Serial Old 收集器

  用于老年代的Serial收集器,单线程,使用“标记-整理”算法。

SerialOld_gc_runtime

 

Parallel Old 收集器

  Parallel Scavenge的老年代版本,多线程,使用“标记-整理”算法。

ParallelOld_gc_runtime

CMS 收集器

  CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。基于“标记-清除”算法。运作过程分为4个阶段:

  • 初始标记(CMS initial mark):值标记GC Roots能直接关联到的对象。
  • 并发标记(CMS concurrent mark):进行GC RootsTracing的过程。
  • 重新标记(CMS remark):修正并发标记期间因用户程序继续运行而导致标记发生改变的那一部分对象的标记,这个阶段停顿时间一般会比初始标记阶段稍长。
  • 并发清除(CMS concurrent sweep):

  其中初始标记和重新标记两个阶段仍然需要Stop-The-World,整个过程中耗时最长的并发标记和并发清除过程中收集器都可以和用户线程一起工作。

CMS_gc_runtime

  CMS的主要优点是:并发收集和低停顿。SUN官方文档把它称之为并发低停顿收集器。

  CMS收集器具有以下三个缺点:

  • CMS收集器对CPU资源非常敏感。在并发阶段,虽然不会导致用户线程停顿,但是因为会占用一部分线程而导致应用程序变慢。CMS默认启动的回收线程是(CPU数量+3)/4.
  • 浮动垃圾(Floating Garbage):由于CMS并发清理阶段用户线程还在运行着,自然会有新的垃圾产生,而这些垃圾是在标记过程之后,CMS只能在下次GC时回收它们,这些垃圾就称为浮动垃圾。CMS收集器无法处理浮动垃圾,可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。在垃圾收集阶段用户线程还在运行,因此需要预留足够的内存给用户线程使用。如果预留内存不能满足用户线程,会出现“Concurrent Mode Failure”,这时虚拟机将启动临时后备预案:临时启用Serial Old收集器来重新进行老年代的垃圾收集。可以通过设置参数-XX:CMSInitiatingOccupancyFraction来设置触发收集器启动阈值。
  • 由于CMS使用的是清除算法,会导致内存碎片问题,因此提供了参数用于控制是否在进行FullGC后进行内存整理,还提供了参数用于控制在多少次FullGC时才进行内存整理。内存整理是不能并发的,也就是要暂停所有用户线程。

G1 收集器

  G1(Garbage First):是一款面向服务端应用的垃圾收集器,用于替换CMS收集器。

G1_gc_runtime

  G1将整个Java堆划分为大小相等的独立区域(Region);新生代和老年代不再是物理隔离的,都由一组不连续的Region组成。

G1的特点:

  • 并行与并发:充分利用多CPU缩短Stop-The-World停顿时间,在收集过程中用并发的方式让Java线程继续执行。
  • 分代收集:仍然有分代的概念,不需要其他收集器配合,独立管理整个GC堆。
  • 空间整合:从整体看,是基于“标记-整理”算法实现的,从局部(两个Region之间)看是基于“复制”算法的。在运行期间不会产生内存碎片。
  • 可预测的停顿:G1跟踪各个Region里垃圾堆积值的价值大小,维护一个优先级队列,每次根据允许的时间,优先回收价值最大的Region。(这也是Garbage First的由来)

  Region之间的对象引用以及其他收集器中的新生代与老年代之间的对象引用,虚拟机都是使用Remembered Set来避免全堆扫描的。G1中每个Region都有一个与之对应的Remembered Set,虚拟机发现程序对引用类型的数据进行写操作时,会产生一个Write Barrier暂时中断写操作,检查引用的对象是否处于不同的Region中(在其他收集器中就是检查是否老年代中的对象引用了新生代中对象),如果是, 通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set中。进行内存回收时,在GC根节点的枚举范围中加入Remembered Set即可保证不对全堆扫描也不会有遗漏。

  G1垃圾回收主要有4个阶段:

  • 初始标记:只标记GC Roots能直接关联到的对象,并且修改TAMS(Next Top at Mark Start)值,让下一阶段用户程序并发运行时,能在正确可用的Region中创建新对象。此阶段需要暂停用户线程。
  • 并发标记:从GC Roots开始对堆中对象进行可达性分析,找出存活对象;耗时较长,可与用户线程并发执行。
  • 最终标记:修正在并发标记期间有变动的标记记录。
  • 刷选回收:对各个Region的回收价值和成本进行排序,根据用户期望的GC停顿时间制定回收计划,进行垃圾回收

GC日志

  每一种收集器的日志形式都是由它们自身的实现所决定的,换而言之,每个收集器的日志格式都可以不一样。但虚拟机设计者为了方便用户阅读,将各个收集器的日志都维持一定的共性,例如以下两段典型的GC日志:
33.125: [GC [DefNew: 3324K->152K(3712K), 0.0025925 secs] 3324K->152K(11904K), 0.0031680 secs]  
100.667: [Full GC [Tenured: 0K->210K(10240K), 0.0149142 secs] 4603K->210K(19456K), [Perm : 2999K->2999K(21248K)], 0.0150007 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]  
  最前面的数字“33.125:”和“100.667:”代表了GC发生的时间,这个数字的含义是从Java虚拟机启动以来经过的秒数。
  GC 日志开头的“[GC”和“[Full GC”说明了这次垃圾收集的停顿类型,而不是用来区分新生代GC还是老年代GC的。如果有“Full”,说明这次GC是发生了Stop-The- World的,例如下面这段新生代收集器ParNew的日志也会出现“[Full GC”(这一般是因为出现了分配担保失败之类的问题,所以才导致STW)。如果是调用System.gc()方法所触发的收集,那么在这里将显示 “[Full GC (System)”。
[Full GC 283.736: [ParNew: 261599K->261599K(261952K), 0.0000288 secs] 
  接下来的“[DefNew”、“[Tenured”、“[Perm”表示GC发生的区域,这里显示的区域名称与使用的GC收集器是密切相关的,例如上面样例所使用的Serial收集器中的新生代名为“Default New Generation”,所以显示的是“[DefNew”。如果是ParNew收集器,新生代名称就会变为“[ParNew”,意为“Parallel New Generation”。如果采用Parallel Scavenge收集器,那它配套的新生代称为“PSYoungGen”,老年代和永久代同理,名称也是由收集器决定的。
  后面方括号内部的“3324K->152K(3712K)”含义是“GC前该内存区域已使用容量-> GC后该内存区域已使用容量 (该内存区域总容量)”。而在方括号之外的“3324K->152K(11904K)”表示“GC前Java堆已使用容量 -> GC后Java堆已使用容量 (Java堆总容量)”。
  再往后,“0.0025925 secs”表示该内存区域GC所占用的时间,单位是秒。有的收集器会给出更具体的时间数据,如“[Times: user=0.01 sys=0.00, real=0.02 secs]”,这里面的user、sys和real与Linux的time命令所输出的时间含义一致,分别代表用户态消耗的CPU时间、内核态消耗的 CPU时间和操作从开始到结束所经过的墙钟时间(Wall Clock Time)。CPU时间与墙钟时间的区别是,墙钟时间包括各种非运算的等待耗时,例如等待磁盘I/O、等待线程阻塞,而CPU时间不包括这些耗时,但当系统有多CPU或者多核的话,多线程操作会叠加这些CPU时间,所以读者看到user或sys时间超过real时间是完全正常的。

垃圾收集器参数总结

-XX:+UserParNewGC 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
posted @ 2016-07-09 11:35  简单爱_wxg  阅读(370)  评论(0编辑  收藏  举报