JAVA虚拟机的7种垃圾收集器

1、垃圾收集器概述

1-1、垃圾收集器组合

 JDK7/8后,HotSpot虚拟机所有收集器及组合(连线),如下图:


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

老年代收集器:Serial Old、Parallel Old、CMS

整堆收集器G1

1-2、并发垃圾收集和并行垃圾收集的区别

(A)、并行(Parallel)

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

(B)、并发(Concurrent)

      用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行);

      用户程序在继续运行,而垃圾收集程序线程运行于另一个CPU上;

1-3、Minor GC和Full GC的区别

(A)、Minor GC

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

      当对象生成,但在Eden申请空间失败时就会触发Minor GC,对Eden区进行GC,清除掉非存活的对象,并且把存活的对象移动到Survivor区中的一个区中。

(B)、Full GC

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

       出现Full GC经常会伴随至少一次的Minor GC。

       如果一个对象不能在Eden中被创建,它会直接被创建在老年代中。如果老年代的空间被占满会触发老年代的GC,也被称为Full GCFull GC是一个压缩处理过程,所以它比Minor GC要慢很多。

2、Serial收集器

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

1、特点

     针对新生代; 采用复制算法; 单线程收集;

     进行垃圾收集时,必须暂停所有工作线程,直到完成; 即会"Stop The World"

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

2、应用场景

    Client模式下;

    优于其他收集的地方:

    简单高效; 对于限定单个CPU的环境来说,Serial收集器没有线程交互(切换)开销,可以获得最高的单线程收集效率。

    Stop the World: JVM在后台自动发起和自动完成的,在用户不可见的情况下,把用户正常的工作线程全部停掉。

3、设置参数

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

3、ParNew收集器

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

1、特点

   除了多线程外,其余的行为、特点和Serial收集器一样

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

2、应用场景

      Server模式下ParNew收集器是一个非常重要的收集器,因为除Serial外,目前只有它能与CMS收集器配合工作

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

3、设置参数

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

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

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

4、Parallel Scavenge收集器

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

1、特点

(A)、有一些特点与ParNew收集器相似

      新生代收集器;

      采用复制算法;

      多线程收集;

(B)、主要特点是:它的关注点与其他收集器不同

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

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

2、应用场景

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

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

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

3、设置参数

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

(A)、"-XX:MaxGCPauseMillis"

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

(B)、"-XX:GCTimeRatio"

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

      GCTimeRatio相当于设置吞吐量大小

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

      1 / (1 + n)

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

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

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

5、Serial Old收集器

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

1、特点

      针对老年代;

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

      单线程收集;

  

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

2、应用场景

      主要用于Client模式;

6、Parallel Old收集器

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

1、特点

      针对老年代;

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

      多线程收集;

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

2、应用场景

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

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

3、设置参数

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

7、CMS收集器

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

1、特点

      针对老年代;

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

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

      并发收集、低停顿;

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

2、应用场景

      与用户交互较多的场景;        

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

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

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

3、设置参数

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

4、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收集器运行示意图如下:

8、G1收集器

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

1、特点

(A)、并行与并发

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

      可以并行来缩短"Stop The World"停顿时间;

      也可以并发让垃圾收集与用户程序同时进行

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

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

      能够采用不同方式处理不同时期的对象;

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

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

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

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

2、应用场景

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

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

3、设置参数

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

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

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

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

4、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中垃圾对象;

9、内存分配与回收策略

     对象的内存分配也就是在堆上分配,主要是分配在新生代的Eden区上, 少数情况下也可能直接分配在老年代中。

1. 优先在Eden区分配

    大多数情况下,对象在新生代Eden区分配,当Eden区空间不足时,会发生Minor GC。

2. 大对象直接进入老年代

    避免在Eden区和两个Survivor区之间发生大量的内存拷贝。

3. 长期存活的对象进入老年代

     JVM为对象定义年龄计数器,经过Minor GC依然存活,并且能被Survivor区容纳的,被移到Survivor区,那么对象的年龄就加1,直到达到阈值(15),对象进入老年区。

4. 动态判断对象的年龄

     如果Survivor区中年龄相同的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代。

5. 空间分配担保












猜你喜欢

转载自blog.csdn.net/peixie9441/article/details/80583775