深入理解JVM------GC的策略及算法

为何要了解GC

        平时工作中,我们或多或少的都会遇到内存溢出或者内存泄漏的问题,如果我们不了解GC的原理,那么遇到问题时,很多时候会让人不知所措,了解了GC,我们遇到这些问题时,就能通过排查,知道问题的原因,从而解决问题。

GC解决的事情

        1、哪些对象需要回收

        2、什么时候回收

        3、如何回收

GC策略采用什么算法

        先让我们回顾下,在上一篇文章中我们已经了解,GC主要发生在java堆和方法区上,也就是全局共享的区域。线程独享(程序计数器、java虚拟机栈、本地方法栈)的区域随着线程而生,随线程而灭,方法结束或者线程结束,内存就跟着回收了,因此这几个区域不必过多的考虑回收问题。

        现在我们再来考虑第一个问题,这个问题可以理解为,如何确定堆上面的对象之中,哪些还“活着”,哪些已经“死去”(即不能再被其它途径使用的对象)。

        有一种实现简单,效率很高的算法,被称作引用计数算法。它有一个致命的缺陷,就是循环引用的对象无法被回收。下面我们看下示例代码:

public class GCTest {

    public Object instance = null;

    public static void main(String[] args) {
        
        GCTest objA = new GCTest();
        GCTest objB = new GCTest(); //1

        objA.instance = objB;
        objB.instance = objA; //2

        objA = null;
        objB = null; //3

        System.gc();
    }
}    

        当方法结束,objA和objB都将作为待回收的对象,而如果我们采用引用计数算法,则上述两个对象永远都无法被回收。

        大致解释一下,在代码中标注了1、2、3三个数字,当第1个地方的语句执行完以后,两个对象的引用计数全部为1。当第2个地方的语句执行完以后,两个对象的引用计数就全部变成了2。当第3个地方的语句执行完以后,也就是将二者全部归为空值以后,二者的引用计数仍然为1。根据引用计数算法的回收规则,引用计数没有归0的时候是不会被回收的。

可达性分析算法

        由于引用计数算法的缺陷,所以在主流的商用jvm一般采用的是可达性分析算法。这种算法的思路是,以“GC Roots”的对象作为起始点,从这些节点开始往下搜索,搜索不到的对象称为不可达对象,将会被判定认为是可回收对象


        就拿上图来说吧,如果采用的是引用计数算法,则object1-7对象均不会被回收,若采用的是可达性分析算法,则object5-7会被回收。

        在java语言中,可以作为GC Roots的对象有以下几种:

        1、虚拟机栈(栈帧中的本地变量表)中引用的对象。

        2、方法区中类静态属性引用的对象。

        3、方法区中常量引用的对象。

        4、本地方法栈中JNI引用的对象。

垃圾收集算法

        可达性分析算法解决的是上面提到的第一个问题,也是最关键的问题,就是哪些对象可以被回收。

        不过GC还要解决后面两个问题,在可达性分析算法的基础上,在现代虚拟机的实现中,垃圾收集算法主要有下面三种:

        标记-清除算法复制算法标记-整理算法,关于这三种算法将在下一篇跟大家一起讨论,也比较好理解。



猜你喜欢

转载自blog.csdn.net/cool_summer_moon/article/details/80334092