前言
在堆里面存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事情就是要确定这些对象之中哪些还“存活”着,哪些已经“死去”,判断对象的生死存活都有那些算法?
引用计数算法
实现原理:给对象中添加一个引用计数器,每当一个地方引用它是,计数器值就加1;任何时刻计数器为0的对象就是不可能再被使用的。
但是主流的Java虚拟机里面没有选用引用计数算法来管理内存,其中主要的原因是它很难解决对象之间相互引用的问题。
如下一个简单的例子:请看测试代码:对象objA和objB都有字段instance,赋值令 objA.instance=objB及objB.instance=objA,实际上这两个对象已经不可能再被访问了,但是他们因为互相引用这对方,导致引用计数都不为0,于是引用计数算法无法通过GC收集器回收他们。
public class ReferenceCountingGC { /** * @Author: luomo * @CreateTime: 2019/11/4 * @Description: 引用计数算法 */ public Object instance = null; private static final int _1MB=1024*1024; /** * 这个成员属性的唯一意义就是占点内存,以便能在GC日志中看清楚是否被回收 */ private byte[] bigSize =new byte[2 * _1MB]; public static void testGC(){ ReferenceCountingGC objA =new ReferenceCountingGC(); ReferenceCountingGC objB =new ReferenceCountingGC(); objA.instance=objB; objB.instance=objA; objA =null; objB =null; System.gc(); } public static void main(String[] args){ testGC(); } }
程序运行结果如下:
从运行结果中可以清楚看出虚拟机并没有因为互相引用就不回收他们,这也从侧面说明虚拟机并不是通过引用计数算法来判断对象是否存活的。
可达性分析算法
实现原理:在主流的语言的主流实现中,比如Java、C#、甚至是古老的Lisp都是使用的可达性分析算法来判断对象是否存活的。
这个算法的核心思路就是通过一些列的“GC Roots”对象作为起始点,从这些对象开始往下搜索,搜索所经过的路径称之为“引用链”。
当一个对象到GC Roots没有任何引用链相连的时候,证明此对象是可以被回收的。如下图所示:
在Java中,可作为GC Roots对象的列表:
Java虚拟机栈中的引用对象。
本地方法栈中JNI(既一般说的Native方法)引用的对象。
方法区中类静态常量的引用对象。
方法区中常量的引用对象。