垃圾回收机制中,引入计数是如何实现的,内部原理是什么,怎么维持对象引用的

来源:互联网 发布:个推公司怎么样知乎 编辑:程序博客网 时间:2024/05/05 13:42

先简单说说Java运行时内存区,划分为线程私有区和线程共享区

1)线程私有区:

  1、 程序计数器,记录正在执行的虚拟机字节码的地址;

  2、拟机栈:方法执行的内存区,每个方法执行时会在虚拟机栈中创建栈帧;

  3、本地方法栈:虚拟机的Native方法执行的内存区;

2)线程共享区:

  1、Java堆:对象分配内存的区域,这是垃圾回收的主战场;

  2、方法区:存放类信息、常量、静态变量、编译器编译后的代码等数据,另外还有一个常量池。当然垃圾回收也会在这个区域工作。


   问题:如下的例子中,如果采用引用计数法,两个对象是不是会被回收,这个问题好多人好多人搞不懂。下面我就给分析一下
   示例:

 public class GcDemo {

 

    public static void main(String[] args) {

        //分为6个步骤

        GcObject obj1 = new GcObject(); //Step1

        GcObject obj2 = new GcObject(); //Step2

 

        obj1.instance = obj2; //Step 3

        obj2.instance = obj1; //Step 4

 

        obj1 = null; //Step 5

        obj2 = null; //Step 6

    }

}

 

classGcObject{

   public Object instance = null;

}


分析:
情况(一)如果采用的是引用计数算法:(OC中使用)
引用计数是唯一一种没有使用根集的垃圾回收算法,该算法使用引用计数器来区分存活对象和不再使用的对象。



·        Step1GcObject实例1的引用计数加1,实例1的引用计数=1

·        Step2GcObject实例2的引用计数加1,实例2的引用计数=1

·        Step3GcObject实例2的引用计数再加1,实例2的引用计数=2

·        Step4GcObject实例1的引用计数再加1,实例1的引用计数=2

         执行到Step 4,则GcObject实例1和实例2的引用计数都等于2

·        Step5:栈帧中obj1不再指向Java堆,GcObject实例1的引用计数减1,结果为1

·        Step6:栈帧中obj2不再指向Java堆,GcObject实例2的引用计数减1,结果为1

          到此,发现GcObject实例1和实例2的计数引用都不为0,那么如果采用的引用计数算法的话,那么这两个实例所占的内存将得不到释放,这便产生             了内存泄露。

  情况(二):可达性算法这是目前主流的虚拟机都是采用GC Roots Tracing算法,JAVA或C#语言都用的是该算法。
这个算法的基本思路就是通过一系列的称为“GC Root”的对象作为起始点,
利用数学中图论知识,从这些节点开始向下搜素,搜索所走过的路径称为应用链,当一个对象到GC Roots没有任何引用链相连时。则证明此对象时不可用的,可以被回收了。如下图对象object5,object6,object7虽然互相有关系,但是没有GCroots可以达到他们。所以他们时可以被回收的对象。如下图:
图中可达对象便是存活对象,而不可达对象则是需要回收的垃圾内存。
这里涉及两个概念,一是GC Roots,一是可达性。
那么,可以作为GC Roots的有:
1、虚拟机栈的栈帧的局部变量表所引用的对象;

2、本地方法栈的JNI所引用的对象;

3、方法区的静态变量和常量所引用的对象;

从上图可看见obj1与obj2都是GC roots,但因为程序的最后有obj1=null,obj2=null导致断开了连接,所以就没有

相通的了。即为不可到达对象。不可到达对象就会被回收了。

所以这个题的结论是:

GcObject实例1和实例2虽然从引用计数虽然都不为0,但从可达性算法来看,都是GC Roots不可达的对象。







0 0
原创粉丝点击