jvm垃圾回收器

一、垃圾回收器分类

  1、次收集器(scavengeGC或者minorGC)

     针对新生代young的收集器,触发的频率非常频繁,回收的效率上也相对较高。一般当Eden区域内存空间分配不足的时候,会触发minorGC。

     程序运行过程中,当new新对象并且需要在Eden区申请分配内存失败时,会触发一次minorGC,对Eden区进行一次清理,清除非存活的对象,并且将存活的对象拷贝的survivor区域中,整理From和To区域的对象。

    此收集动作发生在年轻代的Eden区,不会影响到年老代,由于大部分的new对象都是从Eden区开始的,同时,Eden也不会分配的过大。所以GC的频率上会很高。在算法选择上,优先考虑速度快、效率高的,尽快的回收Eden。

  2、全收集器(FullGC)

    针对老年代old的收集器,频率相对较低。执行效率上,比minorGC慢。一般,出现FullGC都伴随着至少一次的minorGC(老年代中的对象,大部分都是从年轻代过来的幸存对象)。当在老年代中申请分配空间失败时候,触发

    一次FullGC。

    当老年代堆空间满了的时候,会触发FullGC。

       一般,显式的调用system.gc(),通知jvm去触发一次FullGC,至于什么时候执行,不知道。

二、垃圾回收算法及常用匹配

  1、常见的垃圾回收算法

    1.1、引用计数

      若对象有一个引用,则计数加1。删除一个引用时,计数减1。当垃圾回收的时候,对那些计数为0的对象进行清理。此算法缺陷是无法处理对象的循环引用问题。

      

    2.2、复制

      将内存空间划分为两块相等的区域,每次只使用其中的一块,保证一块的干净。在进行垃圾回收的时候,遍历扫描当前使用的区域,将正在使用的对象复制到另外一块去。此算法只处理正在使用的对象,

      且复制到另一块中也能进行相应的内存整理,保证内存的连续性。此算法的缺陷是内存占用较大,需要两倍的内存空间。

      

    2.3、标记-清除(mark-sweep)

      算法分两步:标记过程中,从根节点出发,开始标记可达的对象,标记出所有的被引用的对象。清除过程中,扫描遍历堆空间,将那些没有标记的对象清理掉。

      此算法缺陷是需要暂停整个应用,且会产生大量的内存碎片(保证不了内存的连续性)。

    2.4、标记-整理(mark-compact)

       算法结合了标记-清除和复制算法的优点。分两步执行:标记过程中,从根节点出发,开始标记可达的对象,标记出所有的被引用的对象。整理阶段,扫描遍历堆空间,将那些没有标记的对象清理掉,

       并且将存活的对象复制压缩到堆中的一块,顺序存放,保证内存的连续性。

  2、分代垃圾回收

    2.1、年轻代的收集器

      2.1.1、串行收集器(Serial)

      Serial收集器是HotSpot运行在Client模式下的默认设置。在收集器工作的时候,只用一条线程去处理完成GC操作,且在GC过程中,会暂停其他所有的工作线程(Stop-the-World),只执行GC线程。

      可以使用参数-XX:+UseSerialGC设置打开。

      单线程收集,简单而且高效。

      

      2.2.2、并行收集器(ParNew)

      串行收集器Serial的多线程版本,除了GC工作时是多线程处理,其他特性均一致。老年代启用CMS收集器(-XX: +UseConcMarkSweepGC)时候,年轻代的默认选项。

      在单CPU的的服务器中,由于多线程存在线程切换开销,所以效率上比不上Serial收集器。但是随着CPU核心数的增加,收集的效率上也会提升。可使用用-XX:ParallelGCThreads=<N>控制收集器工作线程数。

      2.2.3、Parallel Scavenge 收集器

      与ParNew类似,使用复制算法,也是并行多线程收集器。

      其他收集器注重时间效率上,Parallel Scavenge关注点在于系统吞吐量,达到一个可控的吞吐量。所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,

      即系统吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。

      GC时间效率越好,就越适用于有大量用户交互的程序设计中,快速的响应能提高用户的体验。而高吞吐量适用于不关注用户交互的系统中,如后台的数据运算。

      主要的参数有三个:-XX:MaxGCPauseMillis控制最大垃圾回收停顿时间,毫秒数。-XX:GCTimeRatio垃圾收集占总时间比率,直接的吞吐量大小,通常整数,范围0~100。

               -XX:+UseAdaptiveSizePolicy启用自适应开关,VM会根据当前系统运行情况收集性能监控信息,动态的调整年轻代大小,Eden和Survivor区域比例,以及对象进入老年代的阈值等。

    2.2、老年代的收集器

      2.2.1、串行收集器(Serial Old)

         Serial 的老年代版本,使用标记-整理算法。

      2.2.2、Parallel Old收集器

        Parallel Scavenge的老年代版本,多线程收集,使用标记-整理算法。同Parallel Scavenge 一样,注重于系统吞吐量。

      2.2.3、CMS收集器(Concurrent Mark Sweep)

        理论上的并行收集器,也就是说理论上GC线程和用户线程可以并行。多线程分阶段回收,某阶段还是会有Stop-the-world,只是会尽可能减少停顿时间。

        使用标记-清除算法,主要分为四步:

          (1)初始标记:标记从根节点GC Roots能直达的对象,会STW。在Java中,可作为GC Roots对象的包括如下几种: 
              ①. 虚拟机栈(栈桢中的本地变量表)中的引用的对象 ; 
              ②. 方法区中的类静态属性引用的对象 ; 
              ③. 方法区中的常量引用的对象 ; 
              ④. 本地方法栈中JNI的引用的对象;

          (2)并发标记:与用户线程同时运行,从初始标记的对象中,继续标记可达的对象

          (3)重新标记:会STW。扫描整个堆空间,包括young和old。为什么要扫描young?如果老年代的对象被新生代引用的话,也会被标记为存活对象。可使用参数-XX:+CMSScavengeBeforeRemark

                   对新生代执行一次回收,然后将存活对象移到幸存区或者老年代,再进行重新标记的时候,幸存者较少,能提高效率。

          (4)并发清理:与用户线程同时运行,清除未标记对象。由于是跟用户线程并发运行,伴随着程序的运行,这段时间可能也会有大量的垃圾产生,但是这部分垃圾在标记之后,所以在

                  本次的垃圾回收中,不会去处理。留待下一次的GC处理。这部分产生的垃圾,就是“浮动垃圾”。

        CMS 默认启动的回收线程数=(CPU 数目+3)4,参数-XX:ParallelGCThreads可设置线程数。

        CMS是一种预处理垃圾回收器,它不能等到old内存用尽时回收,需要在内存用尽前,完成回收操作,否则会导致并发回收失败;所以cms垃圾回收器开始执行回收操作,有一个触发阈值,默认是老年代或永久带达到92%;

        CMS会产生内存碎片,参数-XX:+UseCMSCompactAtFullCollection 搭配使用-XX:CMSFullGCsBeforeCompaction=N。

            -XX:+UseCMSCompactAtFullCollection表示Full GC后执行一次碎片整理,默认值为true

             -XX:CMSFullGCsBeforeCompaction=N表示,多少次Full GC不执行碎片整理后,下一次必会执行一次碎片整理,默认值是0。

    2.3、分区收集 - G1收集器

        

   3、收集器的常用匹配

     

  

原文地址:https://www.cnblogs.com/eric-fang/p/9004125.html