zoukankan      html  css  js  c++  java
  • JVM--GC

    一、垃圾回收

      垃圾回收一般发生在堆或方法区中,也就是线程共享的部分,堆和方法区的内存分配和垃圾回收都是通过垃圾回收器去实现的。

      不同的垃圾回收器对应不同的垃圾回收算法。

    (一)判断算法

      1、判断哪些对象需要回收

        判断哪些对象需要回收主要有引用计数法和根搜索算法。

        引用计数法:给对象添加一个引用计数器,每一次被引用该计数器就会加一,当引用失效时,该计数器就会减一,当计数器的值为0时,就可以进行垃圾回收。其优点就是实现简单、判断效率高;缺点是很难解决循环引用的问题,这也是java语言没有使用该算法的原因。

        根搜索算法:也叫做可达性算法,通过GCRoot作为根节点开始向下搜索,搜索走过的路程称为引用链,当一个对象到GCRoot没有任何一条引用链时,则证明该对象没有被使用。

        可以作为GCRoot的对象:虚拟机栈中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象、本地方法栈中引用的对象,总结就是堆中的对象不能作为GCRoot

      2、垃圾回收过程

        可达性分析后如果对象是不可达的,也并非直接就进行回收,这只是第一次标记,而对象进行回收需要进行两次标记,其中第一次标记就是前面说的使用GCRoot进行搜索后没有引用链的对象进行标记,第二次标记是在第一次标记后紧接着会有一次筛选,筛选的条件是此对象是否有必要执行finalize()方法,在finalize()方法zhong没有重新与GCRoot建立联系的对象,将会被第二次标记,

        第二次标记成功的对象就会被真正的回收,如果在调用finalize方法时,对象与GCRoot重新建立了联系,那么该对象就会逃离本次回收。

      3、方法区回收

        方法去也是有垃圾回收的,主要回收废弃的常量和无用的类,由于回收性价比极低,因此就算满足回收条件也不一定会回收。

        废弃常量:例如字符串常量池,没有对象引用即可回收,同时常量池中类、方法、字段的符号引用也与此类似。

        无用的类:需要满足三个条件:(1)该类的所有实例都已经被收回;(2)该类的ClassLoader已经被收回;(3)该类对应的Class对象不存在任何的引用,不能通过反射产生该类的实例对象

    (二)回收算法

      垃圾回收算法有标记清除算法、复制回收算法、标记整理算法、分代回收算法等。

      1、标记清除算法

        标记清除算法是最基本的算法,分为标记和清楚两个阶段,首先标记出需要清理的对象,在标记完成后,统一回收掉被标记的对象。

        缺点是效率低(标记和清楚效率都不高)及存在内存碎片(会产生大量不连续的内存碎片,导致大对象不能存储,提前触发GC)。

      2、复制回收算法

        为了提高效率,其将内存分为两块大小相等的区域,每次只使用其中的一块,当这一块的内存用完了,就将还存活的对象复制到另外一块上,然后再把已使用的内存清除掉。

        现代商业虚拟机都是使用复制回收算法来对新生代进行垃圾回收的,每次回收时会将Enden区和其中一个S区中存货的对象复制到另一个S区,然后清空Ende区和该S区,在Hotspot中,Eden区和S区的比例为8:2,S0:S1为1:1,因此在新生代中使用的:未使用的比例为9:1,浪费了十分之一的内存,但是却大大提高了效率。

        当S区内存不够使用时,则会依赖老年代进行内存担保。

      3、标记整理算法

        标记过程仍然使用标记清除算法,在清除完毕后,将还存活的对象都向一端移动,然后清理掉边界以外的内存。

        老年代没有区域对其进行内存担保,因此其使用标记整理算法。

      4、分代回收算法

        目前现在的商用虚拟机都是采用这种算法,跟队对象的存活周期将内存划分为几个不同的区域,然后根据每个区域的特点进行不同的垃圾回收。

        新生代:由于每次回收都有大量对象被回收,因此使用复制回收算法。

        老年代:对象存活率高,无其他区域对其进行内存担保,就必须采用标记清除和标记整理算法。

    (三)内存担保机制

      内存担保是在内存分配时,新生代内存不足时,把新生代存活的对象搬到老年代,然后将腾出来的内存用于存放最新的对象。

      在不同的GC机制下,担保机制也略有不同,在Serial+Serial Old组合下,发现存不下就直接启动内存担保,而在Parallel scavenge+Serial Old组合下, 先要看下要分配的内存是都大于Eden区的一半,如果大于,就直接把对象放在老年代,如果不大于,才会开启内存担保。

    (四)GC方式

      GC方式分为minorGC、majorGC和fullGC三种。

      minorGC:新生代的垃圾回收,很快就回收了,使用的是一些垃圾回收比较快的算法,例如复制回收算法。

      majorGC:老年代的垃圾回收,比minorGC慢10倍,因此要尽量避免majorGC。majorGC不是fuuGC,majorGC只针对堆,而fuuGC不但针对堆,还针对方法区。

      fuuGC:整个堆和方法区的垃圾回收;老年代不够用,没有其他内存区域对其进行内存担保,会触发fuuGC;方法区不够用,没有其他内存区域对其进行担保,会触发fuuGC;当新生代无法被老年代成功担保时,也会发生fuuGC

    二、垃圾收集器

    (一)垃圾回收器概述

      为了达到最大性能,基于分代管理和回收算法,并结合回收的时机,JVM实现的垃圾回收器有:串行回收、并行回收、并发标记回收(CMS)和垃圾优先回收(G1)。

      1、串行回收

        串行回收使用单线程进行垃圾回收,在回收时应用程序需要STW。

        新生代通常使用复制算法,老年代通常使用标记压缩算法。

      2、并行回收

        并行是指多条垃圾回收线程并行工作,但此时用户线程仍然处于等待状态。并发是指用户线程和垃圾回收线程同时执行,有可能用户程序运行在一个cpu上,垃圾回收运行在一个cpu上,也有可能用户程序和垃圾回收在同一个cpu上交替执行。

        并行回收也需要SWT。

        新生代使用复制算法,老年代使用标记压缩算法。

      3、并发标记回收

        并发标记回收(CMS)的整个回收期划分为多个阶段:初始标记、并发标记、重新标记、并发清除等。

        在初始标记和重新标记阶段需要SWT,在并发标记和并发清除阶段可以和应用程序一起执行,这个算法通常用于老年代,新生代可以使用并发回收。

      4、垃圾优先回收

        连续的内存将导致垃圾回收时间过长,停顿时间不可控,因此G1将堆拆分为一系列的分区,这样在一段时间内,大部分的垃圾收集器都只针对一部分分区,而不是整个堆或整个老年代。

    (二)垃圾回收器

      垃圾收集器是垃圾回收算法的具体实现(标记清除、复制回收、标记整理、分代收集),不同的虚拟机提供的垃圾回收会有很大差别,Hotspot虚拟机中的8种垃圾回收器:Serial、parNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1、ZGC

      1、Serial收集器(新生代)

        收集区域:新生代

        收集方式:串行

        收集算法:复制算法

        串行收集器的组合:Serial+SerialOld

        优点:简单有效;对于单CPU环境,Serial不需要线程切换的开销,可以获得高效的收集效率;在应用桌面的程序中,可用内存一般不大,可以在短时间内完成垃圾收集。

        应用场景:主要用于client模式、单CPU、桌面应用程序。

        参数配置:-XX:UseSerialGC

      2、Serial Old收集器(老年代)

        收集区域:老年代

        收集方式:串行

        收集算法:标记整理算法

        应用场景:主要用于client模式

      3、ParNew收集器(新生代)

        收集区域:新生代

        收集方式:并行方式

        收集算法:复制算法

        应用场景:在Server‘模式下,ParNew收集器是一个非常重要的收集器,因为除Serial外,目前只有他能与CMS收集器配合工作;ParNew是Serial收集器的多线程版本。

      4、Paralell Scavenge收集器(新生代)

        收集区域:新生代

        收集方式:并行方式

        收集算法:复制算法

        因为与吞吐量关系密切,也被称为吞吐量收集器,并行收集器的组合:Paralell Scavenge + Serial Old,并行收集器是以关注吞吐量为目标的垃圾收集器,也是server模式下的默认收集器配置。

        特点:他的关注点与其他收集器不同,CMS等收集器关注的是用户线程的停顿时间,而Paralell Scavenge关注的目标是达到一个可控的吞吐量。

        应用场景:提高吞吐量为目标,减少垃圾收集时间,让用户代码获得更长的执行时间。适用于多CPU,对于停顿时间没有特别要求的后台任务。

      5、Paralell Old收集器(老年代)

        收集区域:老年代

        收集方式:并行方式

        回收算法:标记整理

        收集组合:Paralell Scavenge + Paralell Old,Paralell Old是Paralell Scavenge的老年代版本,

        应用场景:在server模式下、多CPU的情况下

      6、CMS收集器(老年代)

        收集区域:老年代

        收集方式:并行方式

        收集算法:标记清除

          优点:并发收集、低停顿

        缺点:

          (1)CMS收集器对CPU资源非常敏感,在并发收集阶段,其虽然不会导致用户线程停顿,但是会因为占用了一部分线程而导致应用程序运行变慢,从而导致吞吐量下降。

          (2)CMS无法处理浮动垃圾,可能会导致并发模式故障从而触发full GC(浮动垃圾:由于CMS的并发标记是和应用程序并发执行的,因此会存在需要被收集但是没有标记到的对象,这些对象就只能在下一次GC中被回收,这些对象就称为浮动垃圾)

          (3)CMS使用的收集算法是标记清除,因此可能会存在大量的内存碎片,从而导致full GC

        应用场景:与用户交互较多的场景,希望系统停顿时间短,注重服务的响应时间。

        参数配置:

    -XX:+UseCMSCompactAtFullCollection 强制进行空间碎片整理;CMS 采用标记算法,会产生大量的空间碎片。以上参数就是强制执行一次空间碎片整理,但是空间碎片整理会引发STW。
    -XX:+CMSFullGCsBeforeCompaction 配置经过几次的FullGC进行空间碎片整理
    -XX:+CMSFullGCsBeforeCompaction=10 经过10次FGC后进行空间碎片整理,以降低STW次数

      7、G1收集器

        收集区域:整个堆

        收集方式:并行方式

        收集算法:标记清楚和标记整理。G1收集器并没有在物理内存中使用分代划分,其在物理内存中划分了2048个分区(Region,可以通过-XX:G1HeapRegionSize进行设置),同时其还保留了分代的逻辑。

        JDK7和JDK8默认使用ParalellGC进行垃圾回收,JDK9默认使用G1收集器,如果在JDK7和JDK8开启G1收集器,使用:-XX:+UseG1GC

        G1不会等到内存耗尽或者快要耗尽时才开始进行垃圾回收,而是在内部采用了启发式算法,在老年代找出具有高收集效益的区进行收集。

        G1里面有一个card卡片的概念。G1将堆内存划分为多个内存大小为512Byte的区域,一个区域称为一个card,分配对象会占用物理上连续的几个card;CardTable是一个字节数组,维护者所有的card,card中对象的引用发生变更时,card在cardTable中的值就会被标记为dirty,就称这个card被脏化了。

          G1收集器并没有在物理内存中使用分代划分,其在物理内存中划分了2048个分区(Region,可以通过-XX:G1HeapRegionSize进行设置),同时其还保留了分代的逻辑,但是年轻代和老年代不再是物理上的隔离,他们是一部分Region的集合,这些Region可以是不连续的,每个Region可能随着G1的运行在不同代之间切换。 这些Region分区被分为Eden Region、Survivor Region、Old Region、Humongous Region(巨型分区,当一个对象的大小大于一个Region大小的50%时,他就会独占一个或多个Region,巨型对象会直接分配在老年代,该对象所占用的连续分区被称为巨型分区)。 

        G1的年轻代并不是固定不变的,当现有年轻代分区占满时,JVM会分配新的空闲Region加入到年轻代空间,整个年轻代的空间会在初始空间(-XX:G1NewSizePercent,默认为堆空间大小的5%)和最大空间(-XX:G1MaxNewSizePercent,默认为堆空间的60%)之间动态变化,且有参数目标暂停时间(-XX:MaxGCpauselMillis,默认为200毫秒)、需要扩容的大小及分区的已记忆集合计算得到,当然,G1可以直接设置年轻代的大小(-XX:NewRatio、-XX:Xmn),如果直接设置年轻代大小,则暂停目标将失去意义。

        G1 Young GC

        Young GC主要对Eden区进行垃圾回收,其会在Eden区空间耗尽的情况下触发,这种情况下,Eden空间存活的对象就会被移动到Survivor空间中,如果Survivo空间的空间不足,则这些对象就会被直接晋升到老年代;Survivor空间的对象会被移动到新的Survivor中,也有部分会晋升到老年代,最终Eden空间的数据为空,GC停止工作,应用程序继续执行。

        G1 Mix GC

        Mix GC不仅进行正常的新生代垃圾回收,同时也回收部分后台扫描线程标记的老年代分区。

    (三)垃圾回收器组合方式

      垃圾回收器按照回收区域来划分可以分为:年轻代垃圾回收器、老年代垃圾回收器

      按照收集方式来划分可以分为:串行回收、并行回收、并发回收、垃圾优先

      组合方式:

            

       对于垃圾回收器的选择:

        单CPU或小内存,可以使用SerialGC,-XX:+UserSerialGC

        多CPU,需要大吞吐量,可以使用ParalellGC或者ParalellOldGC,-XX:+UseParalellGC或-XX:UseParalellOldGC

        多CPU,追求低停顿,可以使用ParNew或CMS,-XX:UseParNewGC或-XX:UseConcMarkSweepGC

    ------------------------------------------------------------------
    -----------------------------------------------------------
    ---------------------------------------------
    朦胧的夜 留笔~~
  • 相关阅读:
    LeetCode--Sudoku Solver
    LeetCode--Merge Intervals
    LeetCode--Valid Number
    LeetCode--Max Points on a Line
    1.1
    智能指针原理与简单实现(转)
    C++内存管理(转)
    算法题--扔棋子
    LeetCode--Substring with Concatenation of All Words
    线性代数与MATALB1
  • 原文地址:https://www.cnblogs.com/liconglong/p/14986881.html
Copyright © 2011-2022 走看看