轉摘地址:http://www.cnblogs.com/zwbg/p/6196000.htmlhtml
在java中提供4個級別的引用:強引用、軟引用、弱引用和虛引用。除了強引用外,其餘3中引用都可以在java.lang.ref包中找到對應的類。開發人員能夠在應用程序中直接使用他們。java
1 強引用緩存
強引用就是程序中通常使用的引用類型,強引用的對象是可觸及的,不會被回收。相對的,軟引用、弱引用和虛引用的對象是軟可觸及的、弱可觸及的和虛可觸及的,在必定條件下,都是能夠被回收的。jvm
強引用示例:StringBuffer str = new StringBuffer("Hello world");ide
假設以上代碼是在函數體內運行的,那麼局部變量str將被分配在棧上,而對象StringBuffer實例被分配在堆上。局部變量str指向StringBuffer實例所在堆空間,經過str能夠操做該實例,那麼str就是StringBuffer實例的強引用。函數
此時,若是再運行一個賦值語句:this
StringBuffer str1 = str;.net
則str所指向的對象也將被str1所指向,同時在局部變量表上會分配空間存放str1變量。此時該StringBuffer實例就有兩個引用。對引用的「==」操做用於表示兩操做數所指向的堆空間地址是否相同,不表示兩操做數所指的對象是否相同。線程
強引用的特色:htm
強引用能夠直接訪問目標對象 強引用所指向的對象在任什麼時候候都不會被系統回收,虛擬機寧願拋出OOM異常,也不會回收強引用所指的對象。 強引用可能致使內存泄漏
2 軟引用----可被回收的引用
軟引用是比強引用弱一點的引用類型,一個對象只持有軟引用,那麼當堆空間不足時,就會被回收。軟引用使用java.lang.ref.SoftReference類型
package com.jvm; import java.lang.ref.SoftReference; public class SoftRef { public static class User{ public User(int id,String name){ this.id = id; this.name = name; } public int id; public String name; @Override public String toString() { return "[id="+String.valueOf(id)+",name="+name+"]"; } } public static void main(String[] args) { User u = new User(1,"gim"); SoftReference<User> userSoftRef = new SoftReference<User>(u);//建立引用給定對象的新的軟引用 u = null; //去除強引用
System.out.println(userSoftRef.get()); System.gc(); System.out.println("After GC:"); System.out.println(userSoftRef.get());
byte[] b = new byte[10249257];//分配7M左右,使內存空間緊張 System.gc(); System.out.println(userSoftRef.get()); } }
若是虛擬機不加參數設置堆內存限制,則打印出:
[id=1,name=gim] After GC: [id=1,name=gim] [id=1,name=gim]
由於虛擬機的內存總量是九十多兆,且虛擬機企圖使用的最大內存總量是這個的十幾倍。故不會出現資源緊張,也不會產生內存溢出現象。
若是使用-Xmx10m運行上述代碼,能夠獲得:
[id=1,name=gim](第一次從軟引用中獲取數據) After GC: [id=1,name=gim](GC沒有清除軟引用) null(因爲內存緊張,軟引用被清除)
結論:GC未必會回收軟引用的對象,可是當內存資源緊張時,軟引用對象會被回收,因此軟引用對象不會引發內存溢出。
3 弱引用-----發現即回收
弱引用是一種比軟引用較弱的引用類型。在系統GC時,只要發現弱引用,無論系統堆空間使用狀況如何,都會將對象進行回收。可是因爲垃圾回收器的優先級一般很低,所以,並不必定能很快的發現持有弱引用的對象。在這種狀況下,弱引用對象能夠存在較長的時間,一旦一個弱引用對象被垃圾回收器回收,便會加入一個註冊的引用隊列(這一點和軟引用很像)。弱引用使用java.lang.ref.WeakReference類實現。
實例2:展現弱引用的特色
package com.jvm; import java.lang.ref.WeakReference; public class WeakRef { public static class User{ public User(int id,String name){ this.id = id; this.name = name; } public int id; public String name;
@Override public String toString() { return "[id="+String.valueOf(id)+",name="+name+"]"; } }
public static void main(String[] args) { User u = new User(1,"gim"); WeakReference<User> userReference = new WeakReference<User>(u); u = null; System.out.println(userReference.get()); System.gc(); System.out.println("After GC:"); System.out.println(userReference.get()); } }
不用設置虛擬機參數,直接運行代碼打印出:
[id=1,name=gim] After GC: null
能夠看書,GC以後,弱引用對象當即被清除。弱引用和軟引用同樣,在構造弱引用時,也能夠指定一個引用隊列,當弱引用對象被回收時,就會加入指定的引用隊列,經過這個隊列能夠跟蹤對象的回收狀況。
注意:軟引用、弱引用都很是適合來保存那些無關緊要的緩存數據(應用場景)。若是這麼作,當系統內存不足時,這些緩存數據會被回收,不會致使內存的溢出,而當內存資源充足時,這些緩存又能夠存在至關長的時間,從而起到加速系統的做用。
4 虛引用----對象回收跟蹤
一個持有虛引用的對象,和沒有引用幾乎同樣。隨時均可能被垃圾回收器回收,當試圖經過虛引用的get()方法取得強引用時,老是會失敗。
而且,虛引用必須和引用隊列一塊兒使用,它的做用主要是跟蹤垃圾回收過程
當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會在回收對象後,將這個虛引用加入引用隊列,以通知引用程序對象的回收狀況。
實例3 :使用虛引用跟蹤一個可復活對象的回收
package com.jvm;
import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue;
public class TraceCanReliveObj { public static TraceCanReliveObj obj; //能夠復活對象 static ReferenceQueue<TraceCanReliveObj> phantomQueue = null; public static class CheckRefQueue extends Thread{ @Override public void run() { while(true){ if(phantomQueue!=null){ PhantomReference<TraceCanReliveObj> objt = null; try{ objt = (PhantomReference<TraceCanReliveObj>) phantomQueue.remove(); }catch(InterruptedException e){ e.printStackTrace(); } if(objt!=null){ System.out.println("TraceCanReliveObj is delete by GC"); } } } } }
@Override protected void finalize() throws Throwable { super.finalize(); System.out.println("CanReliveObj finalize called"); obj = this; //使得對象復活 } @Override public String toString() { return "I am CanReliveObj"; } public static void main(String[] args) throws InterruptedException { Thread t = new CheckRefQueue(); t.setDaemon(true);//守護線程 t.start(); phantomQueue = new ReferenceQueue<TraceCanReliveObj>(); obj = new TraceCanReliveObj();
//構造了TraceCanReliveObj對象的虛引用,並指定了引用隊列。 PhantomReference<TraceCanReliveObj> phantomRef = new PhantomReference<TraceCanReliveObj>(obj, phantomQueue); obj = null; //將強引用去除 System.gc(); //第一次進行GC,因爲對象可復活,GC沒法回收該對象。 Thread.sleep(1000); if(obj==null){ System.out.println("obj 是 null"); }else{ System.out.println("obj 可用"); } System.out.println("第二次GC"); obj = null; System.gc(); //第二次進行GC,因爲finalize()只會被調用一次,所以第2次GC會回收對象,同時其引用隊列應該也會捕獲到對象的回收 Thread.sleep(1000); if(obj==null){ System.out.println("obj 是 null"); }else{ System.out.println("obj 可用"); } } }
直接運行代碼打印:
CanReliveObj finalize called (對象復活) obj 可用 第二次GC (第2次,對象沒法復活) TraceCanReliveObj is delete by GC (引用隊列捕獲到對象被回收) obj 是 null
因爲虛引用能夠跟蹤對象的回收時間,所以,也能夠將一些資源釋放操做放置在虛引用中執行和記錄。