《深入理解java虚拟机》之学习笔记(三)——java 判断对象是否可回收

java垃圾回收器在回收时首先要判断对象是否还存活,是否可回收。那么,如何判断一个对象是否可回收呢?本文就这一问题做一个简单的整理。

很容易想到的一个简单的实现方式是给对象添加一个引用计数器,每当有一个地方引用就加1,引用失效则减1;任何时刻计数器为零的对象就是不会再被使用的,内存自动回收时就可以对它进行回收了。在大部分情况下,这确实是一种实现简单且判断效率高的不错的算法,但是这并不是主流的java虚拟机管理内存的算法,其最主要的原因是这种算法很难解决对象之间相互循环引用的问题。

举个简单的例子:

/** * * @author 爱上双眼皮儿的猫 * */ public class ReferenceCountingGC { public Object instance = null; private static final int _1MB = 1024 * 1024; private byte[] bigSize = new byte[2 * _1MB]; public static void tsetGC() { ReferenceCountingGC objA = new ReferenceCountingGC(); ReferenceCountingGC objB = new ReferenceCountingGC(); objA.instance = objB; objB.instance = objA; objA = null; objB = null; System.gc(); } } 


上述例子中objA和objB相互引用,如果使用引用计数器算法来判断对象是否存活,这两个对象是永远不会回收的。但是运行上述代码,结果显示这两个对象虽然都存在引用却还是都被回收,这也说明虚拟机并不是利用引用计数器算法来判断对象是否存活。那么,虚拟机中用的是什么算法呢?

可达性分析算法

顾名思义,该算法通过一系列称为“GC Root” 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Root 没有任何引用链相连接(用图论来说就是从根节点到这个对象节点不可达)时,则证明这个对象是不可用的。如下图所示,对象 5, 6, 7虽然互相有关联,但是它们到GC Root 是不可达的,所以它们将被判定为死亡对象,即可回收对象。

《《深入理解java虚拟机》之学习笔记(三)——java 判断对象是否可回收》


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

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

每个方法执行的时候,虚拟机都会创建一个相应的栈帧(栈帧中包括操作数栈、局部变量表、运行时常量池的引用),栈帧中包含这在方法内部使用的所有对象的引用(当然还有其他的基本类型数据),当方法执行完后,该栈帧会从虚拟机栈中弹出,这样一来,临时创建的对象的引用也就不存在了,或者说没有任何GC ROOTS指向这些临时对象,这些对象在下一次GC时便会被回收掉。

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

静态属性是该类型(class)的属性,不单独属于任何实例,因此该属性自然会作为GC ROOTS的对象。只要这个class存在,该引用指向的对象也会一直存在。class 满足以下条件也是可以被回收的:

a.堆中不存在该类的任何实例

b.加载该类的classloader已经被回收

c.该类的java.lang.Class对象没有在任何地方被引用,也就是说无法通过反射再带访问该类的信息

3.方法区中常量引用得到对象;

4.本地方法栈中JNI(即一般所说的Native方法)引用的对象。

    原文作者:java虚拟机
    原文地址: https://blog.csdn.net/u013399093/article/details/53332136
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞