垃圾收集器Serial 、Parallel、CMS、G1

详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt378

这里介绍4个垃圾收集器,如果进行了错误的选择将会大大的影响程序的性能。

时至今日,仍然有两个事情困扰着开发人员:垃圾收集(GC)和了解异性(程序猿的悲鸣),后者我确实不太了解,因为我被前者搞的无暇顾及怎么了解异性,特别是当知道在JAVA8中对这一区域有了很大的改进和提升还有移除了PermGen和以一些新的令人兴奋的调优。

当我们谈到垃圾回收时,我们绝大多数都知道利用它的概念在我们日常的编程中。但是,当问题出现时,会发现很多是我们不知道的。JVM 最大误区之一就是它只有一个垃圾回收器,实际上是它提供了四个不同的收集器,每个都有其自身独特的优势和劣势。垃圾收集器不是自动选择的,这取决个人以及吞吐量和应用程序的差异。

这些垃圾收集的普遍存在的共同点是他们都把堆分隔成不同的片段来管理,比如在age-old区中的大多数对象应该被快速的回收。这些都是老生常谈的事,我们直接进入主题来看一下各个收集器的不同以及他们的优缺点。

1. Serial收集器

Serial收集器是JAVA虚拟机中最基本、历史最悠久的收集器,在JDK 1.3.1之前是JAVA虚拟机新生代收集的唯一选择。Serial收集器是一个单线程的收集器,但它的“单线程”的意义并不仅仅是说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。

Serial收集器到JDK1.7为止,它依然是JAVA虚拟机运行在Client模式下的默认新生代收集器。它也有着优于其他收集器的地方:简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。在用户的桌面应用场景中,分配给虚拟机管理的内存一般来说不会很大,收集几十兆甚至一两百兆的新生代(仅仅是新生代使用的内存,桌面应用基本上不会再大了),停顿时间完全可以控制在几十毫秒最多一百多毫秒以内,只要不是频繁发生,这点停顿是可以接受的。所以,Serial收集器对于运行在Client模式下的虚拟机来说是一个很好的选择。

PS:开启Serial收集器的方式 -XX:+UseSerialGC

如:Xms30m -Xmx30m -Xmn10m -XX:+UseSerialGC -XX:+PrintGCDetails

-XX:+UseSerialGC的是Serial收集器,Xms30m -Xmx30m 指定了JAVA虚拟机的固定大小为30M,-Xmn10m 指JAVA新生代的空间为10M。

2. Parallel(并行)收集器

这是 JVM 的缺省收集器。就像它的名字,其最大的优点是使用多个线程来通过扫描并压缩堆。串行收集器在GC时会停止其他所有工作线程(stop-the-world),CPU利用率是最高的,所以适用于要求高吞吐量(throughput)的应用,但停顿时间(pause time)会比较长,所以对web应用来说就不适合,因为这意味着用户等待时间会加长。而并行收集器可以理解是多线程串行收集,在串行收集基础上采用多线程方式进行GC,很好的弥补了串行收集的不足,可以大幅缩短停顿时间(如下图表示的停顿时长高度,并发比并行要短),因此对于空间不大的区域(如young generation),采用并行收集器停顿时间很短,回收效率高,适合高频率执行。

图1.Serial收集器与Parallel/ Throughput(并行)收集器的比较

3.CMS收集器

CMS(Concurrent Mark Sweep)收集器是基于“标记-清除”算法实现的,它使用多线程的算法去扫描堆(标记)并对发现的未使用的对象进行回收(清除)。整个过程分为6个步骤,包括:

初始标记(CMS initial mark)

并发标记(CMS concurrent mark)

并发预清理(CMS-concurrent-preclean)

重新标记(CMS remark)

并发清除(CMS concurrent sweep)

并发重置(CMS-concurrent-reset)

其中初始标记、重新标记这两个步骤仍然需要“Stop The World”。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。其他动作都是并发的。

需要注意的是,CMS收集器无法处理浮动垃圾(Floating Garbage),可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。由于CMS并发清理阶段用户线程还在运行着,伴随程序的运行自然还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在本次收集中处理掉它们,只好留待下一次GC时再将其清理掉。这一部分垃圾就称为“浮动垃圾”。也是由于在垃圾收集阶段用户线程还需要运行,即还需要预留足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,需要预留一部分空间提供并发收集时的程序运作使用。在默认设置下,CMS收集器在老年代使用了68%的空间后就会被激活,这是一个偏保守的设置,如果在应用中老年代增长不是太快,可以适当调高参数-XX:CMSInitiatingOccupancyFraction的值来提高触发百分比,以便降低内存回收次数以获取更好的性能。要是CMS运行期间预留的内存无法满足程序需要,就会出现一次“Concurrent Mode Failure”失败,这时候虚拟机将启动后备预案:临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了。所以说参数-XX:CMSInitiatingOccupancyFraction设置得太高将会很容易导致大量“Concurrent Mode Failure”失败,性能反而降低。

还有一个缺点,CMS是一款基于“标记-清除”算法实现的收集器,这意味着收集结束时会产生大量空间碎片。空间碎片过多时,将会给大对象分配带来很大的麻烦,往往会出现老年代还有很大的空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。为了解决这个问题,CMS收集器提供了一个-XX:+UseCMSCompactAtFullCollection开关参数,用于在“享受”完Full GC服务之后额外免费附送一个碎片整理过程,内存整理的过程是无法并发的。空间碎片问题没有了,但停顿时间不得不变长了。虚拟机设计者们还提供了另外一个参数-XX: CMSFullGCsBeforeCompaction,这个参数用于设置在执行多少次不压缩的Full GC后,跟着来一次带压缩的。

该算法与并行收集器的另一个缺点是吞吐量的它使用更多的 CPU,为了使应用程序提供更好的体验,通过使用多个线程来执行扫描和收集。这种情况长时间的运行会使应用程序停顿下来,可以使用提高空间来换取高效的运行。但是,这种算法的使用不是默认的。您必须指定 XX: + USeParNewGC来使用它。如果你可以提供更多的CPU资源的话以避免应用程序暂停,那么你可以使用CMS收集器。假设你的堆的大小小于 4 Gb你必须分配大于 4 GB的资源。

4.G1收集器

G1垃圾收集器在JDK7 update 4之后对大于4G的堆有了更好的支持,G1是一个针对多处理器大容量内存的服务器端的垃圾收集器,其目标是在实现高吞吐量的同时,尽可能的满足垃圾收集暂停时间的要求。G1在执行一些Java堆空间中的全区域操作(如:全局标记)时是和应用程序线程并发进行的,因此减少了Java堆空间的中断比例。(译者注:可简单理解为减少了Stop-the-World的时间比例)。

它与前面的CMS收集器相比有两个显著的改进:一是G1收集器是基于“标记-整理”算法实现的收集器,也就是说它不会产生空间碎片,这对于长时间运行的应用系统来说非常重要。二是它可以非常精确地控制停顿,既能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,具备了一些实时Java(RTSJ)的垃圾收集器的特征。

首先将Java堆空间划分为一些大小相等的区域(region),每个区域都是虚拟机中的一段连续内存空间。G1通过执行并发的全局标记来确定整个Java堆空间中存活的对象。标记阶段完成后,G1就知道哪些区域基本上是空闲的。在回收内存时优先回收这些区域,这样通常都会回收相当数量的内存。这就是为什么它叫做Garbage-First的原因。顾名思义G1关注某些区域的回收和整理,这些区域中的对象很有可能被完全回收。而且G1使用了一个暂停时间预测模型使得暂停时间控制在用户指定的暂停时间内,并根据用户指定的暂停时间来选择合适的区域回收内存。

G1确定了可回收的区域后就是筛选回收(evacuation)阶段了。在此阶段将对象从一个或多个区域复制到单一区域,同时整理和释放内存。该阶段是在多个处理器上多个线程并行进行的,因此减少了暂停时间并提高了吞吐量。G1在每一次的垃圾收集过程中都不断地减少碎片,并能够将暂停时间控制在一定范围内。这些已经是以前的垃圾收集器无法完成的了。比如:CMS收集器并不做内存整理。ParallelOld收集器只是对整个Java堆空间做整理,这样导致相当长的暂停时间。

Java8对G1收集器的优化

在java8 udpate 20中对G1收集器采用了字符串重复消除技术(String deduplication),之前字符串以及内部的char[]数组大量消耗了内存空间,在新的G1垃圾收集器中,将会对内存中重复的字符串进行优化,使他们指向同一个字符数组,以避免相同的字符串出现而使堆处理效率低下,你可以使用 -XX:+UseStringDeduplicationJVM参数来开启。

Java8和PermGen

在 Java 8 最大的变化之一删除了在堆中为类的元数据、内部字符串和静态变量分配 permgen空间的部分。过去如果加载大量的类到内存中经常会出现内存溢出异常,并且开发人员需要在这个方面做大量的工作,所以如果这段通过JVM来管理了将是一个不错误的优化。

每个垃圾收集器都有不同的配置参数,可以通过不同的参数来提升性能和降低吞吐量。这些都取决于你的应用需求,不同的对收集方式、可忍受的停顿时间、内存的大小都不一样,所以要根据自身的需求来定制不同的配置参数。

时间: 2024-10-12 23:32:39

垃圾收集器Serial 、Parallel、CMS、G1的相关文章

JVM垃圾收集器-Serial Old收集器,Parallel Old收集器

Serial Old收集器 ? ? ? Serial Old收集器是Serial收集器的老年代版本,它是一个单线程收集器,使用"标记-整理"算法.这个收集器的主要意义也是被Client模式下的虚拟机使用.在server模式下,它主要还有两大用途:一个是在JDK1.5及之前的版本中与Parallel Scavenge收集器搭配使用,另外一个就是作为CMS收集器的后备元,在并发收集发生 Concurrent Mode Failure的时候使用.? ? ??? ? Parallel Old收

JVM垃圾收集器-Serial收集器

今天我给大家分享的是Serial收集器,垃圾收集器就是内存回收的具体实现.Java虚拟机规范中对垃圾收集器应该如何实现并没有任何规定,因此不同的厂商,不同的版本的虚拟机所提供的垃圾收集器都可能会有很大的差别,并且一般都会提供参数供用户根据自己的应用特点和要求组合出各个年代所使用的收集器,这里讨论的收集器基于Sun HotSpot虚拟机1.6版Update22,这个虚拟机包含的所有的收集器如图所示. ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ?上图展示了7种作用

转 G1垃圾收集器入门

转自:http://blog.csdn.net/zhanggang807/article/details/45956325 最近在复习Java GC,因为G1比较新,JDK1.7才正式引入,比较艰难的找到一篇写的很棒的文章,粘过来mark下.总结这篇文章和其他的资料,G1可以基本稳定在0.5s到1s左右的延迟,但是并不能保证更低的比如毫秒级(金融场景,所以说涉及到钱的,对技术要求真高),号称zing可以(但是一般做到低延时,在其他方面肯定有所损耗,比如吞吐),但是没有实际去研究过这种.另外,G1

G1 垃圾收集器入门

G1 垃圾收集器入门 概览 目的 这个教程覆盖了如何使用G1垃圾收集器和它是怎样被Hotspot JVM使用的,你会学到G1收集器内部是如何工作的,使用G1时的一些关键命令行开关和记录它的操作的一些选项. 完成耗时 大约1小时 介绍 这个OBE(Oracle By Example)覆盖了Java里的Java虚拟机G1垃圾回收的基本概念,在OBE的第一部分, 在介绍垃圾收集器和性能时会附带提供JVM的概览.下一部分回顾一下Hotspot JVM的CMS收集器如何工作.然后,一步一步来指导使用Hot

GC入门指南(三)----垃圾收集器类型

这篇文章我们来学习下所有可用的垃圾收集器类型.java目前有四种类型的垃圾收集器: 1.串行垃圾收集器(Serial Garbage Collector): 2.并行垃圾收集器(Parallel Garbage Collector): 3.CMS垃圾收集器(CMS Garbage Collector); 4.G1垃圾收集器(G1 Garbage Collector); 每种类型都有其优点和缺点,最重要的是我们开发者可以通过jvm参数为JVM选择不同的垃圾收集器.每种垃圾收集器关注点都不同,其带来

JVM垃圾收集器

1.垃圾回收器类型 1.串行垃圾回收器(Serial Garbage Collector) 2. 并行垃圾回收器(Parallel Garbage Collector) 3. 并发标记扫描垃圾回收器(CMS Garbage Collector) 4. G1垃圾回收器(G1 Garbage Collector) 1.串行垃圾回收器(Serial Garbage Collector) 串行垃圾回收器通过持有应用程序所有的线程进行工作.它为单线程环境设计,只使用一个单独的线程进行垃圾的回收,通过冻结所

垃圾收集器与内存分配策略-垃圾收集器

(A).图中展示了7种不同分代的收集器: Serial.ParNew.Parallel Scavenge.Serial Old.Parallel Old.CMS.G1: (B).而它们所处区域,则表明其是属于新生代收集器还是老年代收集器:       新生代收集器:Serial.ParNew.Parallel Scavenge:       老年代收集器:Serial Old.Parallel Old.CMS:       整堆收集器:G1: (C).两个收集器间有连线,表明它们可以搭配使用:

JVM垃圾回收算法和垃圾收集器笔记

概述 程序计数器,本地方法栈,虚拟机栈随线程而生,随线程而灭. Java堆和方法区则不一样,这部分内存分配和回收的都是动态的,垃圾收集器所关注的是这部分内存. 判断对象是否是垃圾的算法 JVM没有选用引用计数算法来管理内存,最主要的是引用计数很难解决对象之间相互循环引用的问题.JVM采用了可达性分析算法来判断对象是否能回收.用GC Roots对象作为起点,向下搜索,搜索走过的路劲称为引用链(reference chain),当对一个对象到GC Roots没有任何引用链时,则证明对象不可用的. 判

垃圾收集器和内存分配

垃圾收集器和内存分配 程序计数器.虚拟机栈.本地方法栈这三个区域和线程的生命周期一致,所以方法结束或者线程结束时,内存自然就跟着回收了.Java堆和方法区,只有在程序处于运行期间才能知道会创建哪些对象,即这部分的内存分配和回收都是动态的,垃圾回收主要关注的是堆内存. 对象存活判断 在进行垃圾回收之前,首先要判断哪些对象还存活,哪些已经死去去.判断对象存活的方法,有如下几种: 引用计数法 每个对象有一个引用计数器,每当有一个地方引用了它计数+1:引用失效计数器-1:当引用计数为0时,说明这个对象在