1 Java引用介绍
Java从1.2版本开始引入了4种引用,这4种引用的级别由高到低依次为:
强引用 > 软引用 > 弱引用 > 虚引用
⑴强引用(StrongReference)
强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。
⑵软引用(SoftReference)
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
⑶弱引用(WeakReference)
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
⑷虚引用(PhantomReference)
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。
由于引用和内存回收关系紧密。下面,先通过实例对内存回收有个认识;然后,进一步通过引用实例加深对引用的了解。
2 内存回收
创建公共类MyDate,它的作用是覆盖finalize()函数:在finalize()中输出打印信息,方便追踪。
说明:finalize()函数是在JVM回收内存时执行的,但JVM并不保证在回收内存时一定会调用finalize()。
MyDate代码如下:
package com.skywang.java; import java.util.Date; public class MyDate extends Date { /** Creates a new instance of MyDate */ public MyDate() { } // 覆盖finalize()方法 protected void finalize() throws Throwable { super.finalize(); System.out.println("obj [Date: " + this.getTime() + "] is gc"); } public String toString() { return "Date: " + this.getTime(); } }
在这个类中,对java.util.Date类进行了扩展,并重写了finalize()和toString()方法。
创建公共类ReferenceTest,它的作用是定义一个方法drainMemory():消耗大量内存,以此来引发JVM回收内存。
ReferenceTest代码如下:
package com.skywang.java; public class ReferenceTest { /** Creates a new instance of ReferenceTest */ public ReferenceTest() { } // 消耗大量内存 public static void drainMemory() { String[] array = new String[1024 * 10]; for(int i = 0; i < 1024 * 10; i++) { for(int j = 'a'; j <= 'z'; j++) { array[i] += (char)j; } } } }
在这个类中定义了一个静态方法drainMemory(),此方法旨在消耗大量的内存,促使JVM运行垃圾回收。
有了上面两个公共类之后,我们即可测试JVM什么时候进行垃圾回收。下面分3种情况进行测试:
情况1:清除对象
实现代码:
package com.skywang.java; public class NoGarbageRetrieve { public static void main(String[] args) { MyDate date = new MyDate(); date = null; } }
运行结果:
<无任何输出>
结果分析:date虽然设为null,但由于JVM没有执行垃圾回收操作,MyDate的finalize()方法没有被运行。
情况2:显式调用垃圾回收
实现代码:
package com.skywang.java; public class ExplicitGarbageRetrieve { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub MyDate date = new MyDate(); date = null; System.gc(); } }
运行结果:
obj [Date: 1372137067328] is gc
结果分析:调用了System.gc(),使JVM运行垃圾回收,MyDate的finalize()方法被运行。
情况3:隐式调用垃圾回收
实现代码:
package com.skywang.java; public class ImplicitGarbageRetrieve { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub MyDate date = new MyDate(); date = null; ReferenceTest.drainMemory(); } }
运行结果:
obj [Date: 1372137171965] is gc
结果分析:虽然没有显式调用垃圾回收方法System.gc(),但是由于运行了耗费大量内存的方法,触发JVM进行垃圾回收。
总结:JVM的垃圾回收机制,在内存充足的情况下,除非你显式调用System.gc(),否则它不会进行垃圾回收;在内存不足的情况下,垃圾回收将自动运行
3、Java对引用的分类
级别 | 什么时候被垃圾回收 | 用途 | 生存时间 |
强引用 | 从来不会 | 对象的一般状态 | JVM停止运行时终止 |
软引用 | 在内存不足时 | 对象简单?缓存 | 内存不足时终止 |
弱引用 | 在垃圾回收时 | 对象缓存 | gc运行后终止 |
虚引用 | Unknown | Unknown | Unknown |
package test05;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import org.junit.Test;
/**
* 1、有输出:代表垃圾回收机制执行时,对象被回收
* 2、垃圾回收机制的优先级别较低,一般不会自动执行
* 3、在内存耗尽时:ReferenceTest.drainMemory();,垃圾回收机制会自动执行
* 4、System.gc(); //显式调用垃圾回收机制,也可让垃圾回收机制执行
* @author Do Not Cry
*
*/
public class NoGarbageRetrieve {
//一、StrongReference
//(一)、强引用 对象依旧被引用:
@Test
public void test04() {
MyDate date = new MyDate();
//ReferenceTest.drainMemory();
System.gc();
}
//没有输出:强引用对象依旧被引用,即使垃圾回收机制显式执行,也不会回收强引用对象
//(二)、强引用 对象没有被引用:
//1、没有耗尽内存,也没有显式调用垃圾回收机制
@Test
public void test01() {
MyDate date = new MyDate();
date = null;
}
//没有输出:说明JVM没有执行垃圾回收操作
//即:强引用的对象,即使不再被引用(date = null),也不会被垃圾回收机制立即回收
//2、耗尽内存,让垃圾回收机制自动执行
@Test
public void test02() {
MyDate date = new MyDate();
date = null;
ReferenceTest.drainMemory();
}
//有输出:内存耗尽,垃圾回收机制自动执行,没有被引用的强引用对象被回收
//3、显示调用垃圾回收机制
@Test
public void test03() {
MyDate date = new MyDate();
date = null;
System.gc();
}
//有输出:垃圾回收机制显式执行,没有被引用的强引用对象被回收
//二、SoftReference
//1、软引用对象仍被引用
@Test
public void teset05() {
SoftReference ref = new SoftReference(new MyDate());
//ReferenceTest.drainMemory(); //即使内存耗尽,垃圾回收机制也不会受仍被引用的软引用对象
System.gc();
}
//没有输出:来及回收机制显式执行,软引用对象没有被回收
//2、软引用对象没有被引用
@Test
public void test06() {
SoftReference ref = new SoftReference(new MyDate());
ref = null;
System.gc();
}
//有输出:软引用对象没有被引用,垃圾回收机制执行时,对象被回收
//三、WeakReference
//1、弱引用对象仍被引用
@Test
public void test07() {
WeakReference ref = new WeakReference(new MyDate());
//ReferenceTest.drainMemory(); //内存耗尽时,会回收软引用对象,机制该对象仍被引用
System.gc();
}
//有输出:即使弱引用仍被引用,在JVM垃圾回收运行时,弱引用被终止.
//2、弱引用对象没有被引用
@Test
public void test08() {
WeakReference ref = new WeakReference(new MyDate());
ref = null;
System.gc();
}
//有输出:弱引用没被引用,在JVM垃圾回收运行时,弱引用被终止.
//四、PhantomReference --垃圾回收情况(同弱引用对象一样)
//1、虚引用对象仍被引用
@Test
public void test09() {
ReferenceQueue queue = new ReferenceQueue();
PhantomReference ref = new PhantomReference(new MyDate(), queue);
System.gc();
}
//有输出:
//2、虚引用对象没有被引用
@Test
public void teset10() {
ReferenceQueue queue = new ReferenceQueue();
PhantomReference ref = new PhantomReference(new MyDate(), queue);
ref = null;
System.gc();
}
//有输出:
}