在堆里面存放着Java世界中几乎所有的对象实例,垃圾收集器在堆堆进行回收前,第一件事情就是要确认这些对象之中哪些还“存活”着,哪些已经“死亡”(“死亡”即不可能再被任何途径使用的对象)。从如果判定对象消亡的角度出发,垃圾收集算法可以划分为引用计数式垃圾收集和追踪式垃圾收集两大类,这两大类也常被称作直接垃圾收集和间接垃圾收集。
引用关系
无论是通过哪种算法判断对象是否存活,其本质都与对象的引用分不开。在JDK1.2版本以后,Java对引用的概念进行了扩充,将其分为强引用(Strongly Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)4种,每种引用强度依次逐渐减弱。
StrongReference:
强引用是最传统的引用的定义,是指在程序代码之中普遍存在的引用赋值,即类似Object obj = new Object()
这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。即当内存空间不足时,JVM宁愿抛出OutOfMemoryError
错误使程序异常终止,也不会回收具有强引用的对象来解决内存不足的问题。如果强引用对象不使用时,需要弱化从而使GC能够回收,例如将其赋值为null
,显式地设置strongReference
对象为null
,或让其超出对象的生命周期范围,则gc
认为该对象不存在引用,这时就可以回收这个对象。具体什么时候收集这要取决于GC
算法。
如果一个对象的强引用是在方法内部,那么这个强引用就存在Java栈中,而真正的引用内容(Object)保存在Java堆中。当这个方法运行完成后,就会推出这个方法栈,则引用对象的引用数为0(或可看成GCRoots不可达),这个对象就会被回收。但如果这个强引用对象是全局变量时,就需要在不使用这个对象时,显示的将其赋值为null。例如ArrayList.clear()方法:
public void clear() {
++this.modCount; Object[] es = this.elementData; int to = this.size; for(int i = this.size = 0; i < to; ++i) {
es[i] = null; } }
在ArrayList
类中定义了一个elementData
数组,在调用clear
方法清空数组时,每个数组元素被赋值为null
。不同于elementData=null
,强引用仍然存在,避免在后续调用add()
等方法添加元素时进行内存的重新分配。使用如clear()
方法内存数组中存放的引用类型进行内存释放特别适用,这样就可以及时释放内存。
SoftReference:
软引用是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK1.2版本之后提供了SoftReference类来实现软引用。软引用可用来实现内存敏感的高速缓存。
// 强引用 String strongReference = new String("abc"); // 软引用 String str = new String("abc"); SoftReference<String> softReference = new SoftReference<String>(str);
软引用可以和一个引用队列(ReferenceQueue
)联合使用。如果软引用所引用对象被垃圾回收,JAVA
虚拟机就会把这个软引用加入到与之关联的引用队列中。
ReferenceQueue<String> r