一、引用计数法
引用计数法顾名思义就是在对象中添加一个引用计数器,每当有其他对象引用它时计数器就+1。大概就是下图这个结构:在线画图工具:ProcessOn
那么引用计数法有什么优点和缺点呢?我想着就是用一小部分空间换时间吧,这样他的效率高,只要计时器为0就可以回收了。但是仔细想想它也有缺点,就是无法解决循环依赖的问题,如下图所示:
此时A与B互相引用着,就算已经没有其他地方引用着A和B类,但是他们之间互相有引用,垃圾回收器就不能回收它们。
此时我们可以做一个实验来测试一下。
这里用的是jdk14,默认是G1垃圾回收器,打印GC日志的参数是-XX:+PrintGCDetails
,此时可以看到垃圾回收器是回收了这两个对象的(Pause Full (System.gc()) 1M->0M(8M)
)。由此可见JVM不是通过引用计数法来判断对象是否已经死翘翘的~这里笔者也没找到资料说明JVM是从什么版本开始弃用引用计数法的或者根本没有使用,有知道的大神可以在评论区说下~
那么JVM到底是怎么判断对象是否已死的呢?这就是要到下面要介绍的可达性分析算法了。
二、可达性分析算法
可达性分析算法就是通过一堆已“GC Roots”的跟对象作为起始节点并且以这些节点开始根据引用关系向下搜索,搜索的过程称为‘引用链’。如果某个对象对GC Roots见没有任何引用关系,则判定改对象死亡。图解如下:
如图。Obj1-Obj4都可以追溯到GC roots对象,所以他们不会被回收;Obj5和Obj6虽然互相引用,但是并没有GC Roots对象引用他们,所以可以判定它们两已经死亡~
三、面试题
1、谈谈在jvm中如何判断一个对象是否死亡
上面已分析,不在重复。
2、如何判断一个常量是否死亡,
此题同java单例对象会被回收吗?
大致相同。在JVM中,判断一个常量是否死亡的条件十分严格,有以下三个条件:
- 该类的所有实例以及其子类实例都已被回收
- 加载该类的类加载器被回收
- 该类的Class对象没有在任何地方被引用,也没有在任何地方通过反射调用该类
只有同时满足以上三个条件的常量才会被回收。所以java单例对象基本上是不会被回收的。
3、什么对象可以作为GC Roots对象?
- 在虚拟机栈中被引用的对象
- 在方法区中类静态属性引用的对象
- 在方法区中常量引用的对象
- 本地方法栈中引用的对象(Native方法)
- Java虚拟机内部的引用,如Class对象,异常对象,类加载器等
- 被同步锁持有的对象
- 反映Java虚拟机内部情况的JMXBean、JVMTi中注册的回答、本地代码缓存等
上面7个是查资料查到的。个人感觉作为普通程序员,记住前六个就好了~