jvm是如何判断对象可以被回收的?
方法一:引用计数法
jvm给对象中添加一个引用计数器,每当有一个地方引用它,计数器加1,当引用失效,计数器减1,任何时候计数器为0的对象就是不可能再被使用的。
此方法存在的局限性:不能解决对象之间相互引用的问题。例如:
如上图所示,栈中引用了堆中对象objA和objB,此时objA和objB的引用计数器都加1;而且把objA赋值给objB的成员变量,objB赋值给objA的成员变量,也就是说objA和objB之间存在引用,此时objA和objB的引用计数器都再加1变成2。而当栈中对objA和objB的引用失效了之后,此时objA和objB实际上已经是无用对象了,但是objA和objB的引用计数器也都只是减1尚且不为0,因此objA和objB两个对象都不会被回收。任然占用堆的内存。这样的情况多了就可能出现内存泄漏问题。
方法二:可达性分析算法
通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。
在Java语言中,可作为GC Roots的对象包括下面几种:
虚拟机栈(栈帧中的本地变量表)中引用的对象。
方法区中类静态属性引用的对象。
方法区中常量引用的对象。
本地方法栈中JNI(即一般说的Native方法)引用的对象
方法三:
常见引用类型:
强引用:普通的变量引用
软引用:将对象用SoftReference软引用类型的对象包裹,正常情况不会被回收,但是GC做完后发现释不出空间存放新的对象,则会把这些软引用的对象回收掉。软引用可用来实现对内存敏感度不高的高速缓存。public static SoftReference<User> user = new SoftReference<User>(new User());
弱引用:将对象用WeakReference弱引用类型的对象包裹,弱引用跟没有引用差不多,GC会直接回收掉,很少用。
public static WeakReference<User> user = new WeakReference<User>(new User());
虚引用:虚引用也成为幽灵引用或幻影引用,它是最弱的一种引用,几乎不用。
方法四:finalize()方法最终判定对象是否存活
即使在可达性分析算法中不可达的对象,也并非是“非死不可”de ,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历再次标记过程。
标记的前提是对象在进行可达性分析后发现没有与GC Roots相连接的引用链。
1、第一次标记并进行一次筛选:
对象没有覆盖finalize()方法,对象将直接被回收。
2、第二次标记:
如果这个对象覆盖了finalize()方法,只要重新与引用链上的任何一个对象建立关联即可。
PS:如何判断一个类是无用的类
类需要同时满足下面3个条件才能算是无用的类:
1、该类所有的实例都已经被回收,也就是堆中不存在该类的任何实例。
2、加载该类的ClassLoader已经被回收。
3、该类对应的java.lang.Class对象没有任何地方呗引用,无法在任何地方通过反射访问该类的方法。