垃圾收集器与内存分配策略之篇二:垃圾收集器

5、垃圾收集器java

   若是说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。因为java虚拟机规范对垃圾收集器实现没有任何的规范所以不一样的厂商,不一样的版本的虚拟机所提供的垃圾收集器都有可能会有很大的区别,而且通常都会提供参数供用户根据本身的应用特色和要求组合出各个年代所使用的收集器。算法

虚拟机中所包含的垃圾收集器以下图:多线程

 

 

 

连线代表明他们能够组合使用。下面分别对以上垃圾收集器进行说明:并发

01)Serial 是历史悠久的收集器,在垃圾回收期间或中断用户线程,是一个单线程的收集器,在进行垃圾收集的时候暂停其余全部的工做线程,直至他收集结束。因为它是在用户不可见的时候暂停线程,这对许多用户来讲都是不可接受的。适合于单个CPU,单线程的状况下面,若是在桌面运行程序下面 即Client模式下面虚拟机来讲是一个很好的选择,由于停顿时间很小。是新生代收集器布局

Serial 收集器的运行过程网站

02)ParNew 收集器线程

  Parnew收集器实际上是serial收集器的多线程版本,除了可使用多线程进行垃圾收集之外,其他行为包括Serial收集器的可用的全部控制参数。Parnew收集器的运行过程示例图:设计

 

须要注意的是除了Serial收集器之外,只有ParNew收集器才能与CMS收集器一块儿工做。ParNew在单核CPU的状况下面绝对不会有比Serial收集器更好的效果,甚至因为存在线程的开销,该收集器在经过超线程技术实现的两个CPU的环境汇都不能百分百的超越serial收集器。固然随着CPU数量的增长,他对于GC时系统资源的有效利用仍是颇有好处的。对象

两种概念的解释:blog

并发:指用户线程与垃圾收集线程同时执行(不必定是并行的,多是交替执行),用户线程在继续运行,而垃圾收集程序运行于另外一个CPU上面。

并行:指多条垃圾收集线程并行执行,但此时用户线程处于等待状态。

03)  Parallel Scavenge收集器

Paraller Scavenge收集器是一个新生代收集器,他也是采用复制算法的收集器,又是并行的多线程收集器。他与ParNew收集器最大的不一样是Parallel Scavenge收集器要达到一个可控的吞吐量。吞吐量= 运行用户代码的时间/(运行用户代码时间+垃圾收集器时间)。如:虚拟机共运行了100分钟,垃圾收集用了1分钟,那么吞吐量是99%。停顿时间越短,用户的体验就会更好。高的吞吐量能够高效率的利用CPU的时间,尽快的完成程序的运算任务,主要适合在后台不须要交互的任务。

 Parallel Scavenge 收集器提供了两个参数用于精确的控制吞吐量,分别是最大垃圾收集停顿时间的-XX:MaxGcPauseMills 参数以及直接设置吞吐量大小的-XX:GCTimeRatio参数。MaxGcPauseMills参数是一个大于0的毫秒数,收集器将尽可能地保证内存回收花费的时间不超过设定值。若是把停顿时间调小,会致使GC次数频繁,吞吐量会降低。

GCTimeRatio参数的值应当是一个大于0且小于100的整数,也就是垃圾收集时间占总数的时间的比率,至关因而吞吐量的倒数。所以Paraller Scavenge收集器也别成为吞吐量优先的收集器。Paraller Scavenge收集器还有一个参数-XX:+UseAdapterSizePolicy值得关注,这是一个自适应策略的参数,一旦打开了之后,就不须要尽进行手动的指定新生代大小,Eden和Survivor区域的比例等细节参数了。虚拟机会根据当前的系统信息动态的调整这些参数,成为GC自适应的调节策略。

04)Serial Old收集器

Serial Old收集器是老年代版本,他一样是一个单线程收集器,这个收集器的主要意义是主要是Client模式下面的虚拟机使用。Serial Old收集器使用的是标记整理算法。用途是:在JDK1.5的版本以前与Paraller Scavenge 收集器搭配使用,另外一种用途是CMS收集器的备选方案。

05) Parallel Old 收集器是Paraller Scavenge 收集器的老年大版本,使用多线程和标记整理算法。由于新生代收集器Parallel Scavenge 收集器没法与CMS收集器一块儿工做,因此若是Parallel Scavenge 收集器选择了在新生代使用,那么老年代只能使用Parallel  Scavenge收集器。Parallel Old 收集器只能和Parallel Scavenge收集器一块儿工做搭配。Serial和 ParNew收集器没法与Parallel Old收集器一块儿工做。

06)CMS 收集器

CMS 收集器是一个以获取最短回收停顿时间为目标的收集器。目前很大一部分的java应用集中在互联网网站或者B/S系统的服务端上,这类应用尤其重视服务的响应速度,但愿停顿时间最短,已给用户最好的体验。CMS收集器就很是符合这类应用的需求。

CMS收集器是基于标记清除算法实现的,它的运做过程相对于前面的几种的收集器来讲更复杂一些。整个过程分为初始标记、并发标记、从新标记、并发清除。其中初始标记和从新标记这两个步骤任然须要“stop the  world“。初始标记仅仅只是标记一下GC Roots 能直接关联到的对象,速度很快.并发标记阶段就是进行GC Root Tracing的过程,而重现标记阶段则是为了修正并发标记期间因用户程序运做而致使的那一部分对象的记录,这个阶段的停顿时间通常会比初始标记的时间稍长一些,但远比并发标记时间短。因为整个过程当中耗时最长的并发标记和并发清除收集线程都是能够与用户线程一块儿工做,因此,从整体来讲,CMS收集器的内存回收过程是与用户线程一块儿并发执行的。经过下图能够清楚的看出CMS收集器的运行过程和须要停顿的时间:

CMS是一款优秀的收集器,它的主要优势在名字上面已经体现出来了:并发收集、低停顿,sun公司的一些官方文档中也称之为并发低停顿收集器。可是CMS收集器还远达不到完美的程度,他有如下三个明显的缺点:

 1.CMS收集器对CPU资源特别敏感。其实,面向并发设计的程序都对CPU资源比较敏感。在并发阶段,他虽然不会致使用户线程的停顿,可是会为了占用了一部分线程而致使应用程序变慢,总吞吐量会下降。CMS默认启动的回收线程数量是(CPU数量+3)/4 ,也就是当CPU在4个以上的时候,并发回收垃圾收集线程很多于25%的CPU资源,并随着CPU的数量的增长而降低。可是当CPU不足4个的时候,好比说两个的时候CMS收集线程对用户的影响就会很大,若是原本负载就比较大的时候,还分出一部分去执行垃圾收集线程,就可能致使用户线程执行变慢,这让人很难接受。为了解决这一个问题,虚拟机提供了一种增量式并发收集器,思想是:在并发标记、清理的时候让GC线程、用户线程交替执行,尽可能减小GC线程独占资源的时间,这样整个垃圾收集的过程会变得更长,可是对用户程序的影响就会小一些,也就是速度降低没有那么明显。

2.CMS收集器没法处理浮动垃圾。因为CMS并发清理阶段是在和用户线程一块儿执行的,伴随着程序的运行天然就还会新的垃圾产生,这一部分垃圾在出如今标记过程以后的话,CMS没法在当次收集过程当中进行处理,只好在下一次的垃圾清理的时候在进行清理,这一部分垃圾成为浮动垃圾。

3.CMS垃圾收集器是基于的标记清除算法,收集结束后会有大量的空间碎片产生。空间碎片过多的时候,将会给大对象分配带来很大的麻烦,每每会出现老年代还有不少的剩余,可是没法找到足够大的连续的空间来分配当前的对象,不得不提早触发一次Full  GC 。为了解决这个问题,CMS收集器提供了一个 -XX:+UseCMSCompactAtFullCollection开关参数(默认就是开启的),用于在CMS收集器顶不住的时候,进行FullGC时候进行内存碎片的合并过程,内存整理过程是没法并发的,空间碎片问题没有了,可是停顿时间会变得很长。为此虚拟机提供了另外一个参数 -XX:CMSFullGCsBeforeCompaction,这个参数是用于设置执行多少次不压缩FullGC,来执行一次带压缩的(默认为0,表示每次full Gc 都进行内存整理)

07) G1收集器

G1是一款面向服务端应用的垃圾收集器。HotSpot开发团队赋予他的使命是(在比较长的时间)将来可替换jdk1.5的CMS收集器。与其余收集器相比,G1收集器的特色:

并行与并发:G1能充分利用多CPU、多核环境下的硬件优点,使用多个CPU来缩短stop-the-world停顿的时间,部分其余收集器本来须要停顿java线程执行的GC动做,G1收集器仍然能够用并发的方式让java线程继续运行。

分代收集:与其余收集器同样,分代概念在G1收集器中得已保留。能够采用不一样的处理方式去处理新建立的对象和已经存活了一段时间的对象。

空间整合:G1收集器是基于标记整理算法实现的垃圾收集器,不会产生空间碎片。

可预测性停顿:这是G1相对于CMS的另外一个优点。G1除了追求低停顿之外,还能创建可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片断内,消耗在垃圾收集收集上的时间不超过N毫秒,这个几乎是已是实时java(RTSJ)的垃圾收集器的特征了。

 

在G1以前的其余收集都是在老年代和新生代之间进行来及回收的,而G1不在是这样。使用G1收集器的话,java的堆内存布局就与其余的收集器有很大的差异,通它将整个java堆划分为多个大小相等的独立区域,虽然还保留着老年代和新生代的概念,可是新生代和老年代不在是物理隔离了,他们都是一部分Region集合。

G1收集器之因此能创建起来可预测的停顿时间模型,是由于它能够有计划的避免在整个java堆中进行全区域的垃圾回收。G1跟踪各个region里面的垃圾堆价值大小,在后台维护一个优先列表,每次都根据容许的收集时间,优先回收价值最大的Region。这种使用Region划份内存空间以及有优先级的区域回收方式,保证了G1收集器在有限的时间内能够获取尽量高的收集效率。

   在G1收集器中,Region之间的对象引用以及其余收集器的新生代与老年代之间的对象引用,虚拟机都是采用Rememberd Set 来避免全局扫描的。G1中每个Region都有一个与之对应的Remember Set,虚拟机发现程序在对Reference 类型的数据进行写操做的时候,会产生一个Write Barrier 暂时中断写操做,检查Reference 引用的对象是否处于不一样的Region中(在分代收集的时候,是检查是否老年代中的对象引用了新生代的对象),若是是,便经过CardTable 把相关的引用信息记录到被引用对象的所属的Region的Remember Set之中。当进行垃圾回收的时候,把GC根节点的枚举范围中加入Rememeber Set便可保证不对全局扫描也不会有遗漏。

初始标记阶段仅仅只是标记一下GC ROOTS能关联的对象,而且能修改TAMS的值,让下一阶段用户程序并发运行时,能在正确可用的Region中建立新的对象,这阶段须要停顿线程,但耗时很短。并发标记阶段是从GC ROOTS开始对堆中进行可达性分析研究,找出存活的对象,这阶段耗时较长,可是能够与用户线程并发执行。而最终标记阶段是为了修正正在并发标记标记期间由于用户线程继续运做而致使标记产生变更的那一部分标记记录,虚拟机将这段变更记录在线程Remember Set Logs 里面,最终标记阶段须要把Remember Set Logs 的数据合并到Remember Set中,这个阶段须要停顿线程,可是能够并行执行。最后筛选回收阶段首先对各,个Region 的回收价值和成本进行排序,根据用户指望的GC 停顿时间来制定回收计划。

相关文章
相关标签/搜索