java強引用,軟引用,弱引用,虛引用
java強引用,軟引用,弱引用,虛引用
原文:https://blog.csdn.net/liaodehong/article/details/52223354
用了Java怎麼長時間一直不知道原來Java還有四種引用型別,這個引用型別和我們平常說的可不一樣。這裡的引用型別不是指資料型別的一種,而是指Java中的引用所分的四種類型。他們代表了JVM回收記憶體的四種強度,分別如下。
強引用:
Java中的引用,有點像C++的指標。通過引用,可以對堆中的物件進行操作。在某函式中,當建立了一個物件,該物件被分配在堆中,通過這個物件的引用才能對這個物件進行操作。
Object o = newObject();
假設以上程式碼是在函式體內執行的,那麼區域性變數o將被分配在棧上,而物件例項,被分配在堆上。區域性變數o指向Object例項所在的堆空間,通過o可以操作該例項,那麼o就是Object的引用。
如果再建立一個賦值語句
Object oj = o;
那麼o指向的Object記憶體空間也會被oj所指向,此時我們可以說oj==o,那麼ob.equals也肯定相等,即兩個物件的值也肯定一樣,當修改了o物件的時候,oj物件也會發生變化,或者當修改了oj物件的時候o物件也肯定會變化。所以如果想複製另外一個物件的值的話千萬不要用這種方式,在多執行緒的情況下可能會有更槽糕的情況發生,你可以使用JDK自帶的克隆方法,也可以重寫克隆方法。
public class Test1 { public static void main(String[] args) { student S=new student("1","我是大s"); student s=S; System.out.println(s==S); //true System.out.println(s.equals(S)); //true System.out.println("打印出大S"+S); //id=1 System.out.println("打印出小s"+s); //id=1 S.setId("2"); System.out.println("修改後的大S"+S); //id=2 System.out.println("未修改後的小s"+s); //id=2 s.setId("3"); System.out.println("未修改後的大S"+S); //id=3 System.out.println("修改後的小s"+s); //id=3 } } class student{ String name; String id; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getId() { return id; } public void setId(String id) { this.id = id; } public student(String name, String id) { super(); this.name = name; this.id = id; } @Override public String toString() { return "student [name=" + name + ", id=" + id + "]"; } }
上例中的大S和小S都是強引用,強引用具有如下特點:
1.強引用可以直接訪問目標物件。
2.強引用所指向的物件在任何時候都不會被系統回收。JVM寧願丟擲OOM異常,也不會回收強引用所指向的物件。
3.強引用可能導致記憶體洩漏,為了避免記憶體洩漏,在使用完成之後我們可以把字串物件設定為null,如果是集合的話可以使用
List list=new ArrayList<>(); list.clear();
軟引用:
軟引用的強度是僅次於強引用的,如果一個物件只具有軟引用,則記憶體空間足夠,垃圾回收器就不會回收它;如果記憶體空間不足了,就會回收這些物件的記憶體。只要垃圾回收器沒有回收它,該物件就可以被程式使用。軟引用可用來實現記憶體敏感的快取記憶體。
我們可以使用java.lang.ref.SoftReference來建立軟引用;
String str=new String("abc"); // 強引用 SoftReference<String> softRef=new SoftReference<String>(str); // 軟引用
當記憶體不足時,等價於:
If(JVM.記憶體不足()) { str = null; // 轉換為空引用 System.gc(); // 垃圾回收器進行回收 }
弱引用:
弱引用的強度比軟引用更次,也就是說只具有弱引用的物件擁有更短暫的生命週期。在垃圾回收器執行緒掃描它所管轄的記憶體區域的過程中,一旦發現了只具有弱引用的物件,不管當前記憶體空間足夠與否,都會回收它的記憶體。不過,由於垃圾回收器是一個優先順序很低的執行緒,因此不一定會很快發現那些只具有弱引用的物件。
如果這個物件是偶爾的使用,並且希望在使用時隨時就能獲取到,但又不想影響此物件的垃圾收集,那麼你應該用 Weak Reference 來標記此物件。
String str=new String("abc"); WeakReference<String> abcWeakRef = new WeakReference<String>(str); str=null;
如果你想把這個物件變成強引用的話可以使用
String abc = abcWeakRef.get();
弱引用可以和一個引用佇列(ReferenceQueue)聯合使用,如果弱引用所引用的物件被垃圾回收,Java虛擬機器就會把這個弱引用加入到與之關聯的引用佇列中。
當你想引用一個物件,但是這個物件有自己的生命週期,你不想介入這個物件的生命週期,這時候你就是用弱引用。
這個引用不會在物件的垃圾回收判斷中產生任何附加的影響。
public class ReferenceTest { private static ReferenceQueue<VeryBig> rq = new ReferenceQueue<VeryBig>(); public static void checkQueue() { Reference<? extends VeryBig> ref = null; while ((ref = rq.poll()) != null) { if (ref != null) { System.out.println("In queue: " + ((VeryBigWeakReference) (ref)).id); } } } public static void main(String args[]) { int size = 3; LinkedList<WeakReference<VeryBig>> weakList = new LinkedList<WeakReference<VeryBig>>(); for (int i = 0; i < size; i++) { weakList.add(new VeryBigWeakReference(new VeryBig("Weak " + i), rq)); System.out.println("Just created weak: " + weakList.getLast()); } System.gc(); try { // 下面休息幾分鐘,讓上面的垃圾回收執行緒執行完成 Thread.currentThread().sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } checkQueue(); } } class VeryBig { public String id; // 佔用空間,讓執行緒進行回收 byte[] b = new byte[2 * 1024]; public VeryBig(String id) { this.id = id; } protected void finalize() { System.out.println("Finalizing VeryBig " + id); } } class VeryBigWeakReference extends WeakReference<VeryBig> { public String id; public VeryBigWeakReference(VeryBig big, ReferenceQueue<VeryBig> rq) { super(big, rq); this.id = big.id; } protected void finalize() { System.out.println("Finalizing VeryBigWeakReference " + id); } }
虛引用
虛引用顧名思義就是形同虛設,虛引用也稱為幻影引用:一個物件是都有虛引用的存在都不會對生存時間都構成影響,也無法通過虛引用來獲取對一個物件的真實引用。唯一的用處:能在物件被GC時收到系統通知,JAVA中用PhantomReference來實現虛引用。
對比不同:
class Grocery { private static final int SIZE = 10000; // 屬性d使得每個Grocery物件佔用較多記憶體,有80K左右 private double[] d = new double[SIZE]; private String id; public Grocery(String id) { this.id = id; } public String toString() { return id; } public void finalize() { System.out.println("即將回收 " + id); }
}
public class References { private static ReferenceQueue<Grocery> rq = new ReferenceQueue<Grocery>(); public static void checkQueue() { Reference<? extends Grocery> inq = rq.poll(); // 從佇列中取出一個引用 if (inq != null) System.out.println("In queue: " + inq + " : " + inq.get()); } public static void main(String[] args) { final int size = 10; // 建立10個Grocery物件以及10個軟引用 Set<SoftReference<Grocery>> sa = new HashSet<SoftReference<Grocery>>(); for (int i = 0; i < size; i++) { SoftReference<Grocery> ref = new SoftReference<Grocery>( new Grocery("軟引用 " + i), rq); System.out.println("剛剛 建立了: " + ref.get()); sa.add(ref); } System.gc(); checkQueue(); // 建立10個Grocery物件以及10個弱引用 Set<WeakReference<Grocery>> wa = new HashSet<WeakReference<Grocery>>(); for (int i = 0; i < size; i++) { WeakReference<Grocery> ref = new WeakReference<Grocery>( new Grocery("弱引用 " + i), rq); System.out.println("剛剛 建立了: " + ref.get()); wa.add(ref); } System.gc(); checkQueue(); // 建立10個Grocery物件以及10個虛引用 Set<PhantomReference<Grocery>> pa = new HashSet<PhantomReference<Grocery>>(); for (int i = 0; i < size; i++) { PhantomReference<Grocery> ref = new PhantomReference<Grocery>( new Grocery("abc " + i), rq); System.out.println("剛剛 建立了: " + ref.get()); pa.add(ref); } System.gc(); checkQueue(); } }
在上面的這個例子中,依次建立了十個10個軟引用、10個弱引用和10個虛引用,它們各自引用一個Grocery物件。並且在建立之後呼叫GC方法。從程式執行時的列印結果可以看出,虛引用形同虛設,它所引用的物件隨時可能被垃圾回收,具有弱引用的物件擁有稍微長的生命週期,當垃圾回收器執行回收操作時,有可能被垃圾回收,具有軟引用的物件擁有較長的生命週期,但在Java虛擬機器認為記憶體不足的情況下,也會被垃圾回收。
總結
強引用:就像是老闆(OOM)的親兒子一樣,在公司可以什麼事都不幹,但是千萬不要老是佔用公司的資源為他自己做事,記得用完公司的妹子之後,要讓她們去工作(資源要懂得釋放) 不然公司很可能會垮掉的。
軟引用:有點像老闆(OOM)的親戚,在公司表現不好有可能會被開除,即使你投訴他(呼叫GC)上班看片,但是隻要不被老闆看到(被JVM檢測到)就不會被開除(被虛擬機器回收)。
弱引用:就是一個普通的員工,平常如果表現不佳會被開除(物件沒有其他引用的情況下),遇到別人投訴(呼叫GC)上班看片,那開除是肯定了(被虛擬機器回收)。
虛引用:這貨估計就是個實習生跟臨時工把,遇到事情的時候想到了你,沒有事情的時候,秒秒鐘拿出去頂鍋,開除。
================= End