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

 

(A)、图中展现了7种不一样分代的收集器:html

       Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1;java

(B)、而它们所处区域,则代表其是属于新生代收集器仍是老年代收集器:算法

      新生代收集器:Serial、ParNew、Parallel Scavenge;服务器

      老年代收集器:Serial Old、Parallel Old、CMS;多线程

      整堆收集器:G1;并发

(C)、两个收集器间有连线,代表它们能够搭配使用oracle

       Serial/Serial Old、Serial/CMS、ParNew/Serial Old、ParNew/CMS、Parallel Scavenge/Serial Old、Parallel Scavenge/Parallel Old、G1;框架

(D)、其中Serial Old做为CMS出现"Concurrent Mode Failure"失败的后备预案(后面介绍);ide

Minor GC和Full GC的区别

(A)、Minor GC布局

       又称新生代GC,指发生在新生代的垃圾收集动做;

       由于Java对象大可能是朝生夕灭,因此Minor GC很是频繁,通常回收速度也比较快;

(B)、Full GC

       又称Major GC或老年代GC,指发生在老年代的GC;

       出现Full GC常常会伴随至少一次的Minor GC(不是绝对,Parallel Sacvenge收集器就能够选择设置Major GC策略);

      Major GC速度通常比Minor GC慢10倍以上;

 并发与并行收集器:垃圾收集器的上下文语境里

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

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

二、Serial收集器

       Serial(串行)垃圾收集器是最基本、发展历史最悠久的收集器;

       JDK1.3.1前是HotSpot新生代收集的惟一选择;

一、特色

      针对新生代;

      采用复制算法;

      单线程收集;

       进行垃圾收集时,必须暂停全部工做线程,直到完成;            

       即会"Stop The World";

      Serial/Serial Old组合收集器运行示意图以下:

二、应用场景

      依然是HotSpot在Client模式下默认的新生代收集器;

      也有优于其余收集器的地方:

      简单高效(与其余收集器的单线程相比);

      对于限定单个CPU的环境来讲,Serial收集器没有线程交互(切换)开销,能够得到最高的单线程收集效率;

      在用户的桌面应用场景中,可用内存通常不大(几十M至一两百M),能够在较短期内完成垃圾收集(几十MS至一百多MS),只要不频繁发生,这是能够接受的

三、设置参数

      "-XX:+UseSerialGC":添加该参数来显式的使用串行垃圾收集器;

四、Stop TheWorld说明

      JVM在后台自动发起和自动完成的,在用户不可见的状况下,把用户正常的工做线程所有停掉,即GC停顿

      会带给用户不良的体验;

      从JDK1.3到如今,从Serial收集器-》Parallel收集器-》CMS-》G1,用户线程停顿时间不断缩短,但仍然没法彻底消除;

三、ParNew收集器

      ParNew垃圾收集器是Serial收集器的多线程版本

一、特色

      除了多线程外,其他的行为、特色和Serial收集器同样;

      如Serial收集器可用控制参数、收集算法、Stop The World、内存分配规则、回收策略等;

      两个收集器共用了很多代码;

      ParNew/Serial Old组合收集器运行示意图以下:

二、应用场景

      在Server模式下,ParNew收集器是一个很是重要的收集器,由于除Serial外,目前只有它能与CMS收集器配合工做

      但在单个CPU环境中,不会比Serail收集器有更好的效果,由于存在线程交互开销。

三、设置参数

      "-XX:+UseConcMarkSweepGC":指定使用CMS后,会默认使用ParNew做为新生代收集器;

      "-XX:+UseParNewGC":强制指定使用ParNew;    

      "-XX:ParallelGCThreads":指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同;

四、为何只有ParNew能与CMS收集器配合

      CMS是HotSpot在JDK1.5推出的第一款真正意义上的并发(Concurrent)收集器,第一次实现了让垃圾收集线程与用户线程(基本上)同时工做;

      CMS做为老年代收集器,但却没法与JDK1.4已经存在的新生代收集器Parallel Scavenge配合工做;

      由于Parallel Scavenge(以及G1)都没有使用传统的GC收集器代码框架,而另外独立实现;而其他几种收集器则共用了部分的框架代码;

      关于CMS收集器后面会详细介绍。

四、Parallel Scavenge收集器

      Parallel Scavenge垃圾收集器由于与吞吐量关系密切,也称为吞吐量收集器(Throughput Collector)

一、特色

(A)、有一些特色与ParNew收集器类似

      新生代收集器;

      采用复制算法;

      多线程收集;

(B)、主要特色是:它的关注点与其余收集器不一样

      CMS等收集器的关注点是尽量地缩短垃圾收集时用户线程的停顿时间;

      而Parallel Scavenge收集器的目标则是达一个可控制的吞吐量(Throughput)

      关于吞吐量与收集器关注点说明详见本节后面;

二、应用场景

      高吞吐量为目标,即减小垃圾收集时间,让用户代码得到更长的运行时间;

      当应用程序运行在具备多个CPU上,对暂停时间没有特别高的要求时,即程序主要在后台进行计算,而不须要与用户进行太多交互

      例如,那些执行批量处理、订单处理、工资支付、科学计算的应用程序;

三、设置参数

      Parallel Scavenge收集器提供两个参数用于精确控制吞吐量:

(A)、"-XX:MaxGCPauseMillis"

      控制最大垃圾收集停顿时间,大于0的毫秒数;

      MaxGCPauseMillis设置得稍小,停顿时间可能会缩短,但也可能会使得吞吐量降低;

      由于可能致使垃圾收集发生得更频繁;

(B)、"-XX:GCTimeRatio"

      设置垃圾收集时间占总时间的比率,0<n<100的整数;

      GCTimeRatio至关于设置吞吐量大小;

      垃圾收集执行时间占应用程序执行时间的比例的计算方法是:

      1 / (1 + n)

      例如,选项-XX:GCTimeRatio=19,设置了垃圾收集时间占总时间的5%--1/(1+19);

      默认值是1%--1/(1+99),即n=99;

垃圾收集所花费的时间是年轻一代和老年代收集的总时间;

若是没有知足吞吐量目标,则增长代的内存大小以尽可能增长用户程序运行的时间;

      此外,还有一个值得关注的参数:

(C)、"-XX:+UseAdptiveSizePolicy"

      开启这个参数后,就不用手工指定一些细节参数,如:

      新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRation)、晋升老年代的对象年龄(-XX:PretenureSizeThreshold)等;

      JVM会根据当前系统运行状况收集性能监控信息,动态调整这些参数,以提供最合适的停顿时间或最大的吞吐量,这种调节方式称为GC自适应的调节策略(GC Ergonomiscs);    

      这是一种值得推荐的方式

      (1)、只需设置好内存数据大小(如"-Xmx"设置最大堆);

      (2)、而后使用"-XX:MaxGCPauseMillis"或"-XX:GCTimeRatio"给JVM设置一个优化目标;

      (3)、那些具体细节参数的调节就由JVM自适应完成;        

      这也是Parallel Scavenge收集器与ParNew收集器一个重要区别;    

四、吞吐量与收集器关注点说明

(A)、吞吐量(Throughput)

      CPU用于运行用户代码的时间与CPU总消耗时间的比值;

      即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间);    

      高吞吐量即减小垃圾收集时间,让用户代码得到更长的运行时间;

(B)、垃圾收集器指望的目标(关注点)

(1)、停顿时间    

      停顿时间越短就适合须要与用户交互的程序;

      良好的响应速度能提高用户体验;

(2)、吞吐量

      高吞吐量则能够高效率地利用CPU时间,尽快完成运算的任务;

      主要适合在后台计算而不须要太多交互的任务;

(3)、覆盖区(Footprint)

      在达到前面两个目标的状况下,尽可能减小堆的内存空间;

      能够得到更好的空间局部性;

 

上面介绍的都是新生代收集器,接下来开始介绍老年代收集器;

五、Serial Old收集器

      Serial Old是 Serial收集器的老年代版本

一、特色

      针对老年代;

      采用"标记-整理"算法(还有压缩,Mark-Sweep-Compact);

      单线程收集;

      Serial/Serial Old收集器运行示意图以下:

二、应用场景

      主要用于Client模式;

      而在Server模式有两大用途:

      (A)、在JDK1.5及以前,与Parallel Scavenge收集器搭配使用(JDK1.6有Parallel Old收集器可搭配);

      (B)、做为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用(后面详解);

六、Parallel Old收集器

      Parallel Old垃圾收集器是Parallel Scavenge收集器的老年代版本;

      JDK1.6中才开始提供;

一、特色

      针对老年代;

      采用"标记-整理"算法;

      多线程收集;

      Parallel Scavenge/Parallel Old收集器运行示意图以下:

二、应用场景

      JDK1.6及以后用来代替老年代的Serial Old收集器;

      特别是在Server模式,多CPU的状况下;

      这样在注重吞吐量以及CPU资源敏感的场景,就有了Parallel Scavenge加Parallel Old收集器的"给力"应用组合;

三、设置参数

      "-XX:+UseParallelOldGC":指定使用Parallel Old收集器;

七、CMS收集器

      并发标记清理(Concurrent Mark Sweep,CMS)收集器也称为并发低停顿收集器(Concurrent Low Pause Collector)或低延迟(low-latency)垃圾收集器;

      在前面ParNew收集器曾简单介绍过其特色;

一、特色

      针对老年代;

      基于"标记-清除"算法(不进行压缩操做,产生内存碎片);            

      以获取最短回收停顿时间为目标;

      并发收集、低停顿;

      须要更多的内存(看后面的缺点);

            

      是HotSpot在JDK1.5推出的第一款真正意义上的并发(Concurrent)收集器;

      第一次实现了让垃圾收集线程与用户线程(基本上)同时工做;

二、应用场景

      与用户交互较多的场景;        

      但愿系统停顿时间最短,注重服务的响应速度;

      以给用户带来较好的体验;

      如常见WEB、B/S系统的服务器上的应用

三、设置参数

      "-XX:+UseConcMarkSweepGC":指定使用CMS收集器;

四、CMS收集器运做过程

      比前面几种收集器更复杂,能够分为4个步骤:

(A)、初始标记(CMS initial mark)

      仅标记一下GC Roots能直接关联到的对象;

      速度很快;

      但须要"Stop The World";

(B)、并发标记(CMS concurrent mark)

      进行GC Roots Tracing的过程;

      刚才产生的集合中标记出存活对象;

      应用程序也在运行;

      并不能保证能够标记出全部的存活对象;

(C)、从新标记(CMS remark)

      为了修正并发标记期间因用户程序继续运做而致使标记变更的那一部分对象的标记记录;

      须要"Stop The World",且停顿时间比初始标记稍长,但远比并发标记短;

      采用多线程并行执行来提高效率;

(D)、并发清除(CMS concurrent sweep)

      回收全部的垃圾对象;

      整个过程当中耗时最长的并发标记和并发清除均可以与用户线程一块儿工做;

      因此整体上说,CMS收集器的内存回收过程与用户线程一块儿并发执行;

      CMS收集器运行示意图以下:

 

        五、CMS收集器3个明显的缺点

                     (A)、对CPU资源很是敏感

      并发收集虽然不会暂停用户线程,但由于占用一部分CPU资源,仍是会致使应用程序变慢,总吞吐量下降。

      CMS的默认收集线程数量是=(CPU数量+3)/4;

      当CPU数量多于4个,收集线程占用的CPU资源多于25%,对用户程序影响可能较大;不足4个时,影响更大,可能没法接受。

 

      增量式并发收集器:

      针对这种状况,曾出现了"增量式并发收集器"(Incremental Concurrent Mark Sweep/i-CMS);

      相似使用抢占式来模拟多任务机制的思想,让收集线程和用户线程交替运行,减小收集线程运行时间;

      但效果并不理想,JDK1.6后就官方再也不提倡用户使用

 

(B)、没法处理浮动垃圾,可能出现"Concurrent Mode Failure"失败

(1)、浮动垃圾(Floating Garbage)

      在并发清除时,用户线程新产生的垃圾,称为浮动垃圾;

      这使得并发清除时须要预留必定的内存空间,不能像其余收集器在老年代几乎填满再进行收集;

      也要能够认为CMS所须要的空间比其余垃圾收集器大;

      "-XX:CMSInitiatingOccupancyFraction":设置CMS预留内存空间;

      JDK1.5默认值为68%;

      JDK1.6变为大约92%;               

(2)、"Concurrent Mode Failure"失败

      若是CMS预留内存空间没法知足程序须要,就会出现一次"Concurrent Mode Failure"失败;

      这时JVM启用后备预案:临时启用Serail Old收集器,而致使另外一次Full GC的产生;

      这样的代价是很大的,因此CMSInitiatingOccupancyFraction不能设置得太大。

(C)、产生大量内存碎片

      因为CMS基于"标记-清除"算法,清除后不进行压缩操做

    "标记-清除"算法介绍时曾说过:

      产生大量不连续的内存碎片会致使分配大内存对象时,没法找到足够的连续内存,从而须要提早触发另外一次Full GC动做。

      解决方法:                

(1)、"-XX:+UseCMSCompactAtFullCollection"

      使得CMS出现上面这种状况时不进行Full GC,而开启内存碎片的合并整理过程;

      但合并整理过程没法并发,停顿时间会变长;

      默认开启(但不会进行,结合下面的CMSFullGCsBeforeCompaction);

(2)、"-XX:+CMSFullGCsBeforeCompaction"

      设置执行多少次不压缩的Full GC后,来一次压缩整理;

      为减小合并整理过程的停顿时间;

      默认为0,也就是说每次都执行Full GC,不会进行压缩整理;

      因为空间再也不连续,CMS须要使用可用"空闲列表"内存分配方式,这比简单实用"碰撞指针"分配内存消耗大;

      整体来看,与Parallel Old垃圾收集器相比,CMS减小了执行老年代垃圾收集时应用暂停的时间;

      但却增长了新生代垃圾收集时应用暂停的时间、下降了吞吐量并且须要占用更大的堆空间;

 

八、G1收集器

      G1(Garbage-First)是JDK7-u4才推出商用的收集器;

一、特色

(A)、并行与并发

      能充分利用多CPU、多核环境下的硬件优点;

      能够并行来缩短"Stop The World"停顿时间;

      也能够并发让垃圾收集与用户程序同时进行;

(B)、分代收集,收集范围包括新生代和老年代    

      能独立管理整个GC堆(新生代和老年代),而不须要与其余收集器搭配;

      可以采用不一样方式处理不一样时期的对象;

                

      虽然保留分代概念,但Java堆的内存布局有很大差异;

      将整个堆划分为多个大小相等的独立区域(Region);

      新生代和老年代再也不是物理隔离,它们都是一部分Region(不须要连续)的集合;

      更多G1内存布局信息请参考:

 

(C)、结合多种垃圾收集算法,空间整合,不产生碎片

      从总体看,是基于标记-整理算法;

      从局部(两个Region间)看,是基于复制算法;

      这是一种相似火车算法的实现;

 

      都不会产生内存碎片,有利于长时间运行;

(D)、可预测的停顿:低停顿的同时实现高吞吐量

      G1除了追求低停顿处,还能创建可预测的停顿时间模型;

      能够明确指定M毫秒时间片内,垃圾收集消耗的时间不超过N毫秒;

二、应用场景

      面向服务端应用,针对具备大内存、多处理器的机器;

      最主要的应用是为须要低GC延迟,并具备大堆的应用程序提供解决方案;

      如:在堆大小约6GB或更大时,可预测的暂停时间能够低于0.5秒;

            

      用来替换掉JDK1.5中的CMS收集器;

      在下面的状况时,使用G1可能比CMS好

      (1)、超过50%的Java堆被活动数据占用;

      (2)、对象分配频率或年代提高频率变化很大;

      (3)、GC停顿时间过长(长于0.5至1秒)。

      是否必定采用G1呢?也未必:

      若是如今采用的收集器没有出现问题,不用急着去选择G1;

      若是应用程序追求低停顿,能够尝试选择G1;

      是否代替CMS须要实际场景测试才知道。

三、设置参数

      "-XX:+UseG1GC":指定使用G1收集器;

      "-XX:InitiatingHeapOccupancyPercent":当整个Java堆的占用率达到参数值时,开始并发标记阶段;默认为45;

      "-XX:MaxGCPauseMillis":为G1设置暂停时间目标,默认值为200毫秒;

      "-XX:G1HeapRegionSize":设置每一个Region大小,范围1MB到32MB;目标是在最小Java堆时能够拥有约2048个Region;

 

四、为何G1收集器能够实现可预测的停顿

      G1能够创建可预测的停顿时间模型,是由于:

      能够有计划地避免在Java堆的进行全区域的垃圾收集;

      G1跟踪各个Region得到其收集价值大小,在后台维护一个优先列表;

      每次根据容许的收集时间,优先回收价值最大的Region(名称Garbage-First的由来);

      这就保证了在有限的时间内能够获取尽量高的收集效率;

五、一个对象被不一样区域引用的问题

      一个Region不多是孤立的,一个Region中的对象可能被其余任意Region中对象引用,判断对象存活时,是否须要扫描整个Java堆才能保证准确?

      在其余的分代收集器,也存在这样的问题(而G1更突出):

      回收新生代也不得不一样时扫描老年代?

      这样的话会下降Minor GC的效率;

      解决方法:

      不管G1仍是其余分代收集器,JVM都是使用Remembered Set来避免全局扫描:

      每一个Region都有一个对应的Remembered Set;

      每次Reference类型数据写操做时,都会产生一个Write Barrier暂时中断操做;

      而后检查将要写入的引用指向的对象是否和该Reference类型数据在不一样的Region(其余收集器:检查老年代对象是否引用了新生代对象);

      若是不一样,经过CardTable把相关引用信息记录到引用指向对象的所在Region对应的Remembered Set中;

                    

      当进行垃圾收集时,在GC根节点的枚举范围加入Remembered Set;

      就能够保证不进行全局扫描,也不会有遗漏。

六、G1收集器运做过程

      不计算维护Remembered Set的操做,能够分为4个步骤(与CMS较为类似)。

(A)、初始标记(Initial Marking)

      仅标记一下GC Roots能直接关联到的对象;

      且修改TAMS(Next Top at Mark Start),让下一阶段并发运行时,用户程序能在正确可用的Region中建立新对象;

      须要"Stop The World",但速度很快;

(B)、并发标记(Concurrent Marking)

      进行GC Roots Tracing的过程;

      刚才产生的集合中标记出存活对象;

      耗时较长,但应用程序也在运行;

      并不能保证能够标记出全部的存活对象;

(C)、最终标记(Final Marking)

      为了修正并发标记期间因用户程序继续运做而致使标记变更的那一部分对象的标记记录;

      上一阶段对象的变化记录在线程的Remembered Set Log;

      这里把Remembered Set Log合并到Remembered Set中;

                    

      须要"Stop The World",且停顿时间比初始标记稍长,但远比并发标记短;

      采用多线程并行执行来提高效率;

(D)、筛选回收(Live Data Counting and Evacuation)

      首先排序各个Region的回收价值和成本;

      而后根据用户指望的GC停顿时间来制定回收计划;

      最后按计划回收一些价值高的Region中垃圾对象;

                    

      回收时采用"复制"算法,从一个或多个Region复制存活对象到堆上的另外一个空的Region,而且在此过程当中压缩和释放内存;

      能够并发进行,下降停顿时间,并增长吞吐量;

      G1收集器运行示意图以下:

 

        

①Serial收集器

最基本、发展历史最悠久,JDK1.3以前是惟一的新生代收集器

单线程处理器:它不只仅只会使用一个CPU或一条收集线程去完成垃圾收集工做,更重要的是在它进行垃圾收集时,必须暂停其余全部的线程工做,直到它收集完成。

如今依然是虚拟机运行在Client模式下的默认新生代收集器。

特色:简单高效,对于限定单个CPU的环境来讲,Serial没有线程开销,专心作垃圾收集天然能够得到最高的单线程收集效率。

 

②ParNew收集器

Serial收集器的多线程版本。运行在Server模式下的新生代收集器首选。除了Serial收集器,目前只有他能与CMS收集器配合工做。

ParNew收集器开启的线程数与CPU的数量相同,在CPU很是多的环境下,可使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数。

③Parallel Scavenge又称为“吞吐量优先”收集器

新生代收集器

特别之处是:

目标是达到一个可控制的吞吐量(吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间))。

 

 Parallel Scavenger收集器提供两个参数来精确控制吞吐量:

 

        1)控制最大垃圾收集停顿时间的:-XX:MaxGCPauseMillis参数;

 

        2)直接设置吞吐量大小的:-XX:GCTimeRatio参数。

 

        另外,Parallel Scavenger收集器还有GC自适应的调节策略(经过-XX:+UseAdaptiveSizePolicy参数来开关)。

④Serial Old收集器

Serial收集器的老年代版本,使用“标记-整理”算法,主要意义是给Client模式下的虚拟机使用,若是在Server模式下使用,则主要用途是:在JKD1.5以及之前的版本中与Parallel Scavenger收集器配合使用。另外一种用途是做为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。

⑤Parallel Old收集器

JDK1.6中提供的。是Parallel Scavenger收集器的老年代版本,使用多线程和“标记-整理”算法。

⑥CMS收集器

 

   CMS(ConcurrentMark Sweep)并发标记清除收集器是一种以获取最短停顿时间为目标的收集器。包含四个步骤:

     使用“标记-清除”算法

        1)初试标记(标记一下GC Roots能直接关联到的对象);

        2)并发标记(GC Roots Tracing(查找引用链)的过程);

    仍然须要“Stop The  World”。

        3)从新标记(修正并发标记阶段引用改变的那一部分对象的标记);

        4)并发清除。

        包含三个缺点:

        1)CMS收集器对CPU资源很是敏感;

        2)没法处理浮动垃圾(FloatingGarbage),可能出现Concurrent Mode Failure失败而致使另外一次Full GC的产生。

        3)收集结束时会产生大量空间碎片。

⑦G1收集器(GarbageFirst)

        G1收集器是面向服务端应用的垃圾收集器。具备的特色是:

        1)并行与并发;

        2)分代收集;

        3)空间整合;

        4)可预测停顿。

相关文章
相关标签/搜索