JVM 垃圾回收机制 引用记数法和可达性分析法

JVM垃圾回收机制  引用计数算法与可达性分析算法


这里对引用计数算法作了较为细致的分析

 

首先需要判断哪些内存需要回收

由于线程私有的虚拟机栈、Native栈、程序计数器都是和线程并存亡的,在线程开始时得到分配,线程结束时线程私有内存被回收,这一块只剩下了如何回收的问题,这个问题留到后边解决。

而方法区之中的类信息要被回收掉,其判定是非常严格的。垃圾回收发生最频繁的地方,应当是在堆之中。而堆中几乎存放了所有的对象实例,首先需要做的就是判断哪些实例需要回收。

 

判断实例是否还存活——引用计数算法与可达性分析法

 

深入分析引用计数算法:

基本实现是:每个对象有一个引用计数器,当一个地方引用该对象时,计数器+1,当该对象的一个引用失效之后,计数器-1。为0时就意为着该对象已不可能再被使用。

这种实现效率较高,且设计简单,但它很难解决对象之间相互循环引用的问题。

那么什么是引用该对象,什么时候该对象的引用失效?

引用对象还比较好理解,就是某一个变量得到了堆中某一实例的引用时,算作一次引用。

引用失效则是指该原本拥有堆中某实例地址的引用的对象,失去了引用,算作引用失效。

这里举一个例子说明,网上也很少有资料谈及,找到一篇知乎的回答说的比较清晰,感觉也有道理,这里对作者的思路简单整理下:

publicstaticvoid
main(String[] args) {
 GcObject obj1 = new GcObject(); //Step1
GcObjectobj 2 = new GcObject();//Step2
obj1.instance= obj2; //Step3
obj2.instance= obj1;////Step4
obj1= null; //Step5
obj2= null; //Step6


}



step1:new 了一个GcObject() 相当于是在堆中把该实例(暂且称作实例1)的引用赋给了obj1  实例1被引用  计数+1

step2:new 了一个GcObject() 相当于是在堆中把该实例(暂且称作实例2)的引用赋给了obj2  实例2被引用  计数+1

step3:obj1.instance属性赋值obj2  obj2是实例2的引用,所以实例2被引用  计数+1

step4:obj2.instance属性赋值obj1  obj1是实例1的引用,所以实例1被引用  计数+1

step5:obj1不再引用实例实例1引用失效一次  计数-1

step6:obj2 不再引用实例 实例 2 引用失效一次   计数 -1


此时实例1和实例2已不可能被引用了,不论是obj1还是obj2 或是他们的instance都随着 = null不再指向实例的引用了。

但此时实例1和实例2的计数器值都为1,发生了内存泄漏的情况。

一个假定情况:这里不是对象之间的相互循环引用,会是什么样?

换句话说,为什么引用计数算法的问题会发生在对象循环相互引用的时候?

原作者把这个问题留给了读者自己解决。


obj3= obj2; //Step3*


obj4= obj1;//Step4*


obj1= null; //Step5*


obj2= null; //Step6*



step3*:obj3赋值obj2  obj2是实例2的引用,所以实例2被引用  计数+1

step4*:obj4赋值obj1  obj1是实例1的引用,所以实例1被引用  计数+1

step5*:obj1不再引用实例实例1引用失效一次  计数-1

step6*:obj2不再引用实例实例2引用失效一次  计数-1



但注意此时,obj3obj4对实例1和实例2的引用仍然被保留,并没有随着obj1obj2 = null 的操作而被清空,所以此时实例1和实例2的计数器都为1是正确的。



由此可见,引用计数算法在很多时候还是很可靠的,因此相当多的技术也采用了引用计数算法。



参考资料:原作者@Gityuan回答:https://www.zhihu.com/question/21539353



可达性分析算法:

这是目前JVM所采用的算法。

该算法通过一系列的GC Roots对象作为起始点,从这些对象向下搜索,与之相连接的就是存活的对象,如果一个对象不能连接到任何一个GC Roots,则该对象将被判为死亡。


GC Roots包括:


1、虚拟机栈中引用的对象

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

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

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

以上是书中提到的,另网上资料说还有存活的Thread类对象,Class类等等的另一种列表,感觉手头的这本书只是对GC Roots分类方法和网络上的说法有所不同,实际上是差不多的东西,比如虚拟机栈中引用的对象就可能包括一些线程对象。



 



 



 



 



猜你喜欢

转载自blog.csdn.net/my_dearest_/article/details/79846881