什么时候回收对象?当然是这个对象再也不会被用到的时候回收。所以要想解决 “什么时候回收?” 这个问题,我们要先能判断一个对象什么时候什么时候真正的 “死” 掉了,判断对象是否可用主要有以下两种方法。
判断对象是否可用的算法
引用计数算法
算法描述
给对象添加一个引用计数器,每有一个地方引用它,计数器加 1。引用失效时,计数器减 1。计数器值为 0 的对象不再可用。
缺点
很难解决循环引用的问题。即 objA.instance = objB; objB.instance = objA;
,objA 和 objB 都不会再被访问后,它们仍然相互引用着对方,所以它们的引用计数器不为 0,将永远不能被判为不可用。
可达性分析算法(主流)
算法描述
从 “GC Root” 对象(根对象)作为起点开始向下搜索,走过的路径称为引用链(Reference Chain),从 “GC Root” 开始,不可达的对象被判为不可用。
Java 中可作为 “GC Root” 的对象:
- 栈中(本地变量表中的 reference)
- 虚拟机栈中,栈帧中的本地变量表引用的对象;
- 本地方法栈中,JNI 引用的对象(native方法);
- 方法区中
- 类的静态属性引用的对象;
- 常量引用的对象;
即便如此,一个对象也不是一旦被判为不可达,就立即死去的,宣告一个的死亡需要经过两次标记过程。
四种引用类型
JDK 1.2 后,Java 中才有了后 3 种引用的实现。
- 强引用: 像
Object obj = new Object()
这种,只要强引用还存在,垃圾收集器就永远不会回收掉被引用的对象。 - 软引用: 用来引用还存在但非必须的对象。对于软引用对象,在 OOM 前,虚拟机会把这些对象列入回收范围中进行第二次回收,如果这次回收后,内存还是不够用,就 OOM。实现类:
SoftReference
。 - 弱引用: 被弱引用引用的对象只能生存到下一次垃圾收集前,一旦发生垃圾收集,被弱引用所引用的对象就会被清掉。实现类:
WeakReference
。 - 虚引用: 幽灵引用,对对象没有半毛钱影响,甚至不能用来取得一个对象的实例。它唯一的用途就是:当被一个虚引用引用的对象被回收时,系统会收到这个对象被回收了的通知。实现类:
PhantomReference
。
宣告对象死亡的两次标记过程
当发现对象不可达后,该对象被第一次标记,并进行是否有必要执行 finalize()
方法的判断;
- 不需要执行:对象没有覆盖
finalize()
方法,或者finalize()
方法已被执行过(finalize()
只被执行一次); - 需要执行:将该对象放置在一个队列中,稍后由一个虚拟机自动创建的低优先级线程执行。
finalize()
方法是对象逃脱死亡的最后一次机会,不过虚拟机不保证等待 finalize()
方法执行结束,也就是说,虚拟机只触发 finalize()
方法的执行,如果这个方法要执行超久,那么虚拟机并不等待它执行结束,所以最好不要用这个方法。
finalize()
方法能做的,try-finally 都能做,所以忘了这个方法吧!
方法区的回收
永久代的 GC 主要回收:废弃常量 和 无用的类。
- 废弃常量:例如一个字符串 “abc”,当没有任何引用指向 “abc” 时,它就是废弃常量了。
- 无用的类:同时满足以下 3 个条件的类。
- 该类的所有实例已被回收,Java 堆中不存在该类的任何实例;
- 加载该类的 Classloader 已被回收;
- 该类的 Class 对象没有被任何地方引用,即无法在任何地方通过反射访问该类的方法。