在JVM中,垃圾回收器作为管理jvm内存空间的模块,需要对是否回收某个对象的内存空间进行判断,因此java中定义了引用类型作为垃圾回收机制的判断标准。
JAVA中的四种引用类型:强引用 软引用 弱引用 虚引用
Reference类
强引用:把一个对象赋给一个引用变量,这个引用变量就是一个强引用,表明对象是可达的。被强引用的对象不能被垃圾回收机制回收,是造成内存泄漏的主要原因之一。一般我们使用的都是强引用。
其余三种引用一般都与引用队列共同使用
软引用:软引用需要用SoftReference实现,内存空间不足时回收,内存足够时候不回收(类似于家里闲置的物品)
/**
* 软引用
* 只有当内存不够的时候,才回收这类内存,因此在内存足够的时候,它们通常不被回收
*
* <pre>
* 无论是否发送GC,执行结果都是:
* java.lang.Object@f9f9d8
* null
* java.lang.Object@f9f9d8
* null
* </pre>
*
* 可以看到:只有发送了GC,将对于从内存中释放的时候,JVM才会将reference加入引用队列
*/
@Test
public void soft()throws Exception
{
Object obj = new Object();
ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();
SoftReference<Object> softRef = new SoftReference<Object>(obj, refQueue);
System.out.println(softRef.get()); // java.lang.Object@f9f9d8
System.out.println(refQueue.poll());// null
// 清除强引用,触发GC
obj = null;
System.gc();
System.out.println(softRef.get());
Thread.sleep(200);
System.out.println(refQueue.poll());
}
弱引用:WeakReference实现,类似软引用,但总会被回收
/**
* 弱引用(WeakReference): 当发生GC的时候,Weak引用对象总是会内回收回收。因此Weak引用对象会更容易、更快被GC回收。
* Weak引用对象常常用于Map数据结构中,引用占用内存空间较大的对象
*
* <pre>
* 如果不发生垃圾回收:
* java.lang.Object@f9f9d8
* null
* java.lang.Object@f9f9d8
* null
*
* 如果发生垃圾回收:
* java.lang.Object@f9f9d8
* null
* null
* java.lang.ref.WeakReference@422ede
*
* <pre>
*/
@Test
public void weak() throws Exception
{
Object obj = new Object();
ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();
WeakReference<Object> weakRef = new WeakReference<Object>(obj, refQueue);
System.out.println(weakRef.get()); // java.lang.Object@f9f9d8
System.out.println(refQueue.poll());// null
// 清除强引用,触发GC
obj = null;
System.gc();
System.out.println(weakRef.get());
// 这里特别注意:poll是非阻塞的,remove是阻塞的.
// JVM将弱引用放入引用队列需要一定的时间,所以这里先睡眠一会儿
// System.out.println(refQueue.poll());// 这里有可能是null
Thread.sleep(200);
System.out.println(refQueue.poll());
// System.out.println(refQueue.poll());//这里一定是null,因为已经从队列中移除
// System.out.println(refQueue.remove());
}
虚引用:PhantomReference, ,它不能单独使用,必须和引用队列联合使用。虚 引用的主要作用是跟踪对象被垃圾回收的状态。
/**
* 当GC一但发现了虚引用对象,将会将PhantomReference对象插入ReferenceQueue队列.
* 而此时PhantomReference所指向的对象并没有被GC回收,而是要等到ReferenceQueue被你真正的处理后才会被回收.
*
* <pre>
* 不发生GC执行结果是:
* null
* null
* null
* null
*
* 发生GC执行结果是:
* null
* null
* null
* java.lang.ref.PhantomReference@87816d
* </pre>
*
* 虚引用在实现一个对象被回收之前必须做清理操作是很有用的,比finalize()方法更灵活
*/
@Test
public void phantom() throws Exception
{
Object obj = new Object();
ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();
PhantomReference<Object> phantom = new PhantomReference<Object>(obj,refQueue);
System.out.println(phantom.get()); // java.lang.Object@f9f9d8
System.out.println(refQueue.poll());// null
obj = null;
System.gc();
// 调用phanRef.get()不管在什么情况下会一直返回null
System.out.println(phantom.get());
// 当GC发现了虚引用,GC会将phanRef插入进我们之前创建时传入的refQueue队列
// 注意,此时phanRef所引用的obj对象,并没有被GC回收,在我们显式地调用refQueue.poll返回phanRef之后
// 当GC第二次发现虚引用,而此时JVM将phanRef插入到refQueue会插入失败,此时GC才会对obj进行回收
Thread.sleep(200);
System.out.println(refQueue.poll());
}