1. 程式人生 > >java強引用,軟引用,弱引用,虛引用

java強引用,軟引用,弱引用,虛引用

java強引用,軟引用,弱引用,虛引用

原文:https://blog.csdn.net/liaodehong/article/details/52223354 

 

用了Java怎麼長時間一直不知道原來Java還有四種引用型別,這個引用型別和我們平常說的可不一樣。這裡的引用型別不是指資料型別的一種,而是指Java中的引用所分的四種類型。他們代表了JVM回收記憶體的四種強度,分別如下。

 

強引用:
Java中的引用,有點像C++的指標。通過引用,可以對堆中的物件進行操作。在某函式中,當建立了一個物件,該物件被分配在堆中,通過這個物件的引用才能對這個物件進行操作。

Object o = new
Object();

假設以上程式碼是在函式體內執行的,那麼區域性變數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