美文网首页JVM
关于GC之二-常用GC算法

关于GC之二-常用GC算法

作者: AlanKim | 来源:发表于2019-01-27 09:06 被阅读15次

    回收

    回收是个比较大的话题,有各种各样的算法,针对不同区域,也有不同的算法选择。

    常见的GC算法

    标记清除(Mark-Sweep):

    最基础的GC算法,将需要回收的对象做标记,之后扫描,对标记为可清除的对象进行回收。

    清理完成之后会产生随便。是CMS是基础

    复制(Copying)

    标准应用就是young代的两个S区,互为From、To,复制后,清除From及eden中所有空间。如果To空间不足,则需要对象晋升到Old代。

    标记-整理(Mark-Compact)

    实现上与标记清除算法前半段一样,都是先标记。但是在清除时,是先将标记过的不需要回收的对象移动到一起,使得内存连续,这样,只要将标记边界之外的内存空间清理掉就好了。解决了碎片化问题。

    GC实现的搭配使用

    关于GC的实现及搭配使用,先上一个图:

    1353773614_1052.jpg

    可以看到,除了G1是自己包办天下外,其他的GC算法都是新生代+老年代搭配起来用的,这跟各个分代中的对象特性有一定关系。下面详细说明:

    Serial GC:
    • 顾名思义,串行GC。单线程回收机制,在做回收时,会停掉其他所有操作,即STW。
    • 适用于单CPU、新生代空间很小以及对暂停时间不是很敏感的应用上
    • 使用JVM args:-XX:+UseSerialGC 来指定使用。
    • 同样有Serial Old算法,用于老年代收集,但是会STW很久。
    ParNew GC:
    • 在SerialGC的基础上加入了多线程,但是在GC的时候一样需要STW,只是GC是多个线程在同时进行而已,提高了效率。
    • 使用JVM args: -XX:+UseParNewGC来指定使用
    • 只在Young代收集
    Parallel Scavenge:
    • 在扫描和复制过程采用多线程,与parNew很类似,但是侧重点不同。
    • parNew主要侧重于停顿时间尽量少,不至于使用户一直等待。而parallel scavenge则侧重于达到一个可控制的吞吐量(Throughput)。也被称为吞吐量优先收集器。
    • 所谓吞吐量就是CPU用于运行用户代码时间与CPU总消耗时间的比值。吞吐量=运行用户代码时间/运行用户代码时间+垃圾收集时间。
    • 使用JVM args: -XX:+UseParallelGC来指定使用
    • 使用JVM args: -XX:ParallelGCThreads=4 来指定线程数
    • 对应的Parallel Old GC 用于老年代收集
    CMS
    • Concurrent Mark Sweep
    • 目标是为了解决Serial GC的停顿问题,达到最短回收时间。
    • 从其缩写可以看到,基于Mark-Sweep算法实现的,当然也就会有碎片化问题。
    • 整个收集过程分为以下五个大步骤:
      • 初始化标记,CMS initial mark,标记GC Roots能直接关联到的对象,需要STW,执行速度很快
      • 并发标记,CMS concurrent mark,进行GC Roots Tracing,一条条链路直到叶子节点,并发执行,不影响业务。
      • 重新标记,CMS remark,修正并发标记执行中,线程继续执行产生的标记变化,需STW,比初始化标记时间久,但是远小于并发标记耗时。
      • 并发清除,CMS concurrent sweep,根据标记执行并发清除,不需要STW。
      • 并发reset,恢复初始化设置,不需要stw,耗时非常短
    • CMS缺点
      • 对CPU资源非常敏感,在并发阶段,虽然不会导致用户线程停顿,但是会占用cpu资源,从而导致应用变慢,总吞吐量下降。
      • 默认的回收线程数是: (cpu-kernel-count + 3)/ 4。也就是说,四核cpu,那么就是 (4+3)/4 = 1(取整)
      • 无法处理浮动垃圾
      • 由于GC过程中,用户线程继续在执行,所以要预留足够的内存空间给用户线程使用。因此CMS不能像其他GC收集器那样等到老年代快被填满了再进行收集,需要预留一部分空间,默认在Old代 68%的空间被使用时就会触发CMS。
      • 可以使用JVM args: -XX:CMSInitiatingOccupancyFraction来指定触发CMS的百分比,比如提高百分比,减少CMS GC触发的次数,提高性能。该参数必须配合-XX:UseCMSInitiatingOccupancyOnly使用才有效
      • 但是如果预留的内存太少,无法满足应用运行的内存需要,那么就会出现Concurrent Mode Failure 异常
      • 出现Concurrent Mode Failure时,JVM会临时启用Serial Old GC,来重新进行Old代的收集,这样STW时间就会很长。
      • 基于Mark-Sweep,会产生很多碎片。在有大对象需要分配空间时,内存无法找到连续空间来分配,不得不提前偿触发一次Full GC。
      • 使用JVM args: -XX:UseCMSCompactAtFullCollection在Full GC之后增加一个压缩过程
      • 使用JVM args: -XX:CMSFullGCBeforeCompaction=? 参数设置执行多少次不需要压缩的CMS Full GC后,进行一次压缩操作
    • 浮动垃圾 floating garbage:并发清理阶段产生的garbage,本次gc已经无法处理,只能等待下次处理。
    • GC Roots:
      • 虚拟机栈(栈帧中局部变量表)中引用的对象
      • 方法区中类静态属性引用的对象
      • 方法区中常量引用的对象
      • Native Method Stack中JNI中引用的对象。
    G1

    基于标记整理,Mark-Compact,不会产生内存碎片。

    分region,region维度进行GC,而不是在young、old、方法区整个维度进行整理。

    相关文章

      网友评论

        本文标题:关于GC之二-常用GC算法

        本文链接:https://www.haomeiwen.com/subject/idhjjqtx.html