垃圾收集器在对堆进行垃圾回收前,第一件事就是判断这些对象是否存活着。判断是否存活着有两种算法:引用计数算法和可达性分析算法
引用计数算法
给每个对象添加一个引用计数器,该对象被N个引用所引用,计数器值为N。引用增加时,值+1,引用失效时,值-1。当值为0的时候,说明该对象上所有的引用皆已失效,可以认为该对象不再存活。
这个算法挺好理解的,但有一个缺点,无法解决相互引用对方的情况。如objA和objB都有字段instance,使objA.instance=objB及objB.instance=objA并且objA=null及objB=null,按理说这两个对象应该失效,但objA的instance又引用着对象B,objB的instance又引用着对象A,导致计数器不为0,无法回收。
可达性算法
这是比较主流的算法。通过GC Roots 作为起始点,往下搜索,搜索走过的路径被称为引用链(Reference Chain),当所有引用链遍历完以后,没出现在任何一个引用链上的对象则被视为不可用。以下对象可作为GC Roots:
- 虚拟机栈(本地变量表)中引用的对象
- 方法区静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中Native方法引用的对象
如上图所示object1、object2、object3都在以GC Roots为起始点的引用链上,这三个引用的对象则存活。object4、object5则没在任意一个以GC Roots为起始点的引用链上,这两个引用的对象则失效。
即使是可达性算法不可达的对象,也不一定就会被立即回收,会给予一次机会。第一次检查不可达,会对其进行标记并进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法,如果没有覆盖finalize(),或finalize()已被调用过,则被认为没有必要执行。
如果被判断需要执行finalize()方法,则将对象放入F-Queue的队列中,并在之后由一个虚拟机自动创建的、低优先级的FinaLizer线程去执行它。如果一个对象在执行finalize()方法时运行缓慢或者死循环,则其他对象会永远等待甚至造成内存回收系统的崩溃。
finalize()是对象唯一可以自救的机会,若在finailze()方法将对象重新加入引用链中,则可以在第二次标记中被移除“即将回收”的集合。否则,错过了自救的机会,在第二次标记时就真的被销毁了。
注:finalize()在两次标记中只会被调用一次。并不鼓励覆盖finalize()方法,不确定性大,很容易失去控制,作为了解即可。
网友评论