美文网首页
Java虚拟机(GC使用的算法)

Java虚拟机(GC使用的算法)

作者: wenou | 来源:发表于2017-10-17 18:49 被阅读5次

    垃圾收集器GC

    这里说的GC算法分为 :
    a.确认对象是否 "存活" 的算法,
    b.对象 "已死",要怎样去收回的算法

    确认对象是否"存活" 算法

    1. 引用计数算法
    2. 可达性分析算法(GC 使用)

    引用计数算法
    引用计数算法判断对象是否存活的算法是这样的:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。

    但是Java虚拟机没有用引用计数算法来管理内存,其中最主要的原因是它很难解决对象之间相互循环引用的问题。

    ------引用计数算法的缺陷------

    /**
    *testGC()方法执行后,objA和objB会不会被GC呢?
    */
    public class ReferenceCountingGC{
       public Object instance=null;
       //这个成员属性的唯一意义就是占点内存,以便能在GC日志中看清楚是否被回收过
       private static final int_1MB=1024*1024;
    
       private byte[]bigSize=new byte[2*_1MB];
       public static void testGC(){
           ReferenceCountingGC objA=new ReferenceCountingGC();
           ReferenceCountingGC objB=new ReferenceCountingGC();
           objA.instance=objB;
           objB.instance=objA;
           objA=null;
           objB=null;
           //假设在这行发生GC,objA和objB是否能被回收?
           System.gc();
    }}
    

    运行结果发现,GC对objA和objB都回收了,这也从侧面说明虚拟机并不是通过引用计数算法来判断对象是否存活的。

    可达性分析算法(GC使用)
    在主流的商用程序语言(Java、C#,甚至包括前面提到的古老的Lisp)的主流实现中,都是称通过可达性分析(Reachability Analysis)来判定对象是否存活的。

    这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的

    如下图 : 对象object 5、object 6、object 7虽然互相有关联,但是它们到GC Roots是不可达的,所以它们将会被判定为是可回收的对象

    可达性分析算法

    在Java语言中,可作为GC Roots的对象包括下面几种:

    虚拟机栈(栈帧中的本地变量表)中引用的对象。(即: 方法中的本地变量)
    方法区中类静态属性引用的对象。
    方法区中常量引用的对象。
    本地方法栈中JNI(即一般说的Native方法)引用的对象。

    垃圾收集算法

    1). 标记—清除算法
    算法分为标记和清除两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象

    缺点:一个是效率问题,标记和清除两个过程的效率都不高;另一个是空间问题,标记清楚之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后再程序运行过程中需要分配较大的对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作

    2). 复制算法
    他将可用内存按照容量划分为大小相等的两块,每次只使用其中的一块。当这块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可

    缺点:将内存缩小为了原来的一半

    3). 标记—整理算法
    让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存,和标记—清除算法类似,但是不会产生内存碎片.

    4). 分代收集算法
    只是根据对象存活周期的不同将内存划分为几块。一般是把java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记清理或者标记整理算法来进行回收

    垃圾收集器分类

    a)Serial收集器:
    这个收集器是一个单线程的收集器,但它的单线程的意义不仅仅说明它会只使用一个COU或一条收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集时,必须暂停其他所有的工作线程,直到它手机结束

    b)ParNew 收集器:
    Serial收集器的多线程版本,除了使用了多线程进行收集之外,其余行为和Serial收集器一样

    并行:指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态
    并发:指用户线程与垃圾收集线程同时执行(不一定是并行的,可能会交替执行),用户程序在继续执行,而垃圾收集程序运行于另一个CPU上

    c)Parallel Scavenge :

    收集器是一个新生代收集器,它是使用复制算法的收集器,又是并行的多线程收集器。

    吞吐量:就是CPU用于运行用户代码的时间与CPU总消耗时间的比值。即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)

    d)Serial Old 收集器:
    是Serial收集器的老年代版本,是一个单线程收集器,使用标记整理算法

    e)Parallel Old 收集器:
    Parallel Old是Paraller Seavenge收集器的老年代版本,使用多线程和标记整理算法

    f)CMS收集器:
    CMS收集器是基于标记清除算法实现的,整个过程分为4个步骤:
    1.初始标记2.并发标记3.重新标记4.并发清除
    优点:并发收集、低停顿
    缺点:
    1.CMS收集器对CPU资源非常敏感,CMS默认启动的回收线程数是(CPU数量+3)/4,
    2.CMS收集器无法处理浮动垃圾,可能出现Failure失败而导致一次Full G场地产生
    3.CMS是基于标记清除算法实现的

    g)G1收集器:
    它是一款面向服务器应用的垃圾收集器
    1.并行与并发:利用多CPU缩短STOP-The-World停顿的时间
    2.分代收集
    3.空间整合:不会产生内存碎片
    4.可预测的停顿
    运作方式:初始标记,并发标记,最终标记,筛选回收

    相关文章

      网友评论

          本文标题:Java虚拟机(GC使用的算法)

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