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()); }