JVMGC算法和相应的垃圾回收器

Posted shemlo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVMGC算法和相应的垃圾回收器相关的知识,希望对你有一定的参考价值。

四种算法

引用计数(循环引用不能解决)

复制(新生代)

标记-整理

标记-清除

四种主要的垃圾回收器

技术图片

Serial串行回收:为单线程生产环境设计并使用一个线程进行回收,会暂停所有的用户线程,不适合服务器环境

技术图片技术图片

 砸瓦鲁多!!!

parallel并行回收:多个垃圾收集器线程并行执行,此时用户线程暂停,适用于科学大数据计算

技术图片

 

 

CMS并发标记-清除:用户线程和垃圾回收线程并发执行,不需要停止用户线程,互联网常用,适用于对响应时间要求较高。

从名字(包含“Mark Sweep”)上就可以看出,CMS收集器是基于“标记—清除”算法实现 的,它的运作过程相对于前面几种收集器来说更复杂一些,整个过程分为4个步骤,包括: 初始标记(CMS initial mark) 并发标记(CMS concurrent mark) 重新标记(CMS remark) 并发清除(CMS concurrent sweep) 其中,初始标记、重新标记这两个步骤仍然需要“Stop The World”。初始标记仅仅只是 标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC RootsTracing 的过程,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变 动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远 比并发标记的时间短。 由于整个过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起 工作,所以,从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。通 过图3-10可以比较清楚地看到CMS收集器的运作步骤中并发和需要停顿的时间。技术图片

 

三个明显缺点:

  • CMS收集器对CUP非常敏感。其实,面向并发设计的程序都对CPU资源比较敏感。 在并发阶段,它虽然不会导致用户线程停顿,但是会因为占用了一部分线程(或者说CPU资 源)而导致应用程序变慢,总吞吐量会降低。CMS默认启动的回收线程数是(CPU数量 +3)/4,也就是当CPU在4个以上时,并发回收时垃圾收集线程不少于25%的CPU资源,并且 随着CPU数量的增加而下降。但是当CPU不足4个(譬如2个)时,CMS对用户程序的影响就 可能变得很大,如果本来CPU负载就比较大,还分出一半的运算能力去执行收集器线程,就 可能导致用户程序的执行速度忽然降低了50%,其实也让人无法接受。为了应付这种情况, 虚拟机提供了一种称为“增量式并发收集器”(Incremental Concurrent Mark Sweep/i-CMS)的 CMS收集器变种,所做的事情和单CPU年代PC机操作系统使用抢占式来模拟多任务机制的思 想一样,就是在并发标记、清理的时候让GC线程、用户线程交替运行,尽量减少GC线程的 独占资源的时间,这样整个垃圾收集的过程会更长,但对用户程序的影响就会显得少一些, 也就是速度下降没有那么明显。实践证明,增量时的CMS收集器效果很一般,在目前版本 中,i-CMS已经被声明为“deprecated”,即不再提倡用户使用。
  • CMS收集器无法处理浮动垃圾(Floating Garbage),可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。由于CMS并发清理阶段用户线程还在运行着,伴 随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法 在当次收集中处理掉它们,只好留待下一次GC时再清理掉。这一部分垃圾就称为“浮动垃圾”。也是由于在垃圾收集阶段用户线程还需要运行,那也就还需要预留有足够的内存空间 给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进 行收集,需要预留一部分空间提供并发收集时的程序运作使用。在JDK 1.5的默认设置 下,CMS收集器当老年代使用了68%的空间后就会被激活,这是一个偏保守的设置,如果在 应用中老年代增长不是太快,可以适当调高参数-XX:CMSInitiatingOccupancyFraction的值来 提高触发百分比,以便降低内存回收次数从而获取更好的性能,在JDK 1.6中,CMS收集器 的启动阈值已经提升至92%。要是CMS运行期间预留的内存无法满足程序需要,就会出现一 次“Concurrent Mode Failure”失败,这时虚拟机将启动后备预案:临时启用Serial Old收集器来 重新进行老年代的垃圾收集,这样停顿时间就很长了。所以说参数-XX:CM SInitiatingOccupancyFraction设置得太高很容易导致大量“Concurrent Mode Failure”失败,性能 反而降低。
  • 还有最后一个缺点,在本节开头说过,CMS是一款基于“标记—清除”算法实现的收集器,如果读者对前面这种算法介绍还有印象的话,就可能想到这意味着收集结束时会有大量空间碎片产生。空间碎片过多时,将会给大对象分配带来很大麻烦,往往会出现老年代还有很大空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。为了解决这个问题,CMS收集器提供了一个-XX:+UseCMSCompactAtFullCollection开 关参数(默认就是开启的),用于在CMS收集器顶不住要进行FullGC时开启内存碎片的合并整理过程,内存整理的过程是无法并发的,空间碎片问题没有了,但停顿时间不得不变长。 虚拟机设计者还提供了另外一个参数-XX:CMSFullGCsBeforeCompaction,这个参数是用于设置执行多少次不压缩的Full GC后,跟着来一次带压缩的(默认值为0,表示每次进入Full GC时都进行碎片整理)。

G1:G1是一款面向服务端应用的垃圾收集器。

  • 并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU(CPU或者 CPU核心)来缩短Stop-The-World停顿的时间,部分其他收集器原本需要停顿Java线程执行的 GC动作,G1收集器仍然可以通过并发的方式让Java程序继续执行。
  • 分代收集:与其他收集器一样,分代概念在G1中依然得以保留。虽然G1可以不需要其 他收集器配合就能独立管理整个GC堆,但它能够采用不同的方式去处理新创建的对象和已 经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。
  • 空间整合:与CMS的“标记—清理”算法不同,G1从整体来看是基于“标记—整理”算法实 现的收集器,从局部(两个Region之间)上来看是基于“复制”算法实现的,但无论如何,这 两种算法都意味着G1运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。这种 特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一 次GC。
  • 可预测的停顿:这是G1相对于CMS的另一大优势,降低停顿时间是G1和CMS共同的关 注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一 个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实 时Java(RTSJ)的垃圾收集器的特征了。

在G1之前的其他收集器进行收集的范围都是整个新生代或者老年代,而G1不再是这 样。使用G1收集器时,Java堆的内存布局就与其他收集器有很大差别,它将整个Java堆划分 为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和 老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合

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

如果不计算维护Remembered Set的操作,G1收集器的运作大致可划分为以下几个步骤:

初始标记(Initial Marking)

并发标记(Concurrent Marking)

最终标记(Final Marking)

筛选回收(Live Data Counting and Evacuation)

对CMS收集器运作过程熟悉的读者,一定已经发现G1的前几个步骤的运作过程和CMS 有很多相似之处。初始标记阶段仅仅只是标记一下GC Roots能直接关联到的对象,并且修改 TAMS(Next Top at Mark Start)的值,让下一阶段用户程序并发运行时,能在正确可用的 Region中创建新对象,这阶段需要停顿线程,但耗时很短。并发标记阶段是从GC Root开始 对堆中对象进行可达性分析,找出存活的对象,这阶段耗时较长,但可与用户程序并发执 行。而最终标记阶段则是为了修正在并发标记期间因用户程序继续运作而导致标记产生变动 的那一部分标记记录,虚拟机将这段时间对象变化记录在线程Remembered Set Logs里面,最 终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set中,这阶段需要停顿线 程,但是可并行执行。最后在筛选回收阶段首先对各个Region的回收价值和成本进行排序, 根据用户所期望的GC停顿时间来制定回收计划,从Sun公司透露出来的信息来看,这个阶段 其实也可以做到与用户程序一起并发执行,但是因为只回收一部分Region,时间是用户可控 制的,而且停顿用户线程将大幅提高收集效率。通过图3-11可以比较清楚地看到G1收集器的 运作步骤中并发和需要停顿的阶段。

技术图片

 

 

Java默认的垃圾回收器:-XX:+UseParallelGC

  • UseSerialGC(UseSerialOldGC被废弃)
  • UseConcMarkSweepGC
  • UseParallelGC
  • UseParallelOldGC
  • UseParNewGC
  • UseG1GC

 

以上是关于JVMGC算法和相应的垃圾回收器的主要内容,如果未能解决你的问题,请参考以下文章

GC垃圾回收机制

GC垃圾回收 | 深入理解G1垃圾收集器和GC日志

JVM 之GC垃圾回收器参数设置

JVM GC日志文件分析

JVM GC日志文件分析

深入理解JVM虚拟机3:垃圾回收器详解