1. 程式人生 > >5.9 j(java學習筆記)強軟弱虛引用及WeakHashMap、IdentityHashMap、EnumMap

5.9 j(java學習筆記)強軟弱虛引用及WeakHashMap、IdentityHashMap、EnumMap

一、引用分類

強:執行垃圾回收機制後也不回收,程式出現記憶體溢位也不回收。

軟:在垃圾回收機制執行時判斷記憶體是否已滿,如果記憶體已滿則回收,記憶體充足則不回收。

弱:垃圾回收機制執行後不論記憶體是否充足都會立即回收。

虛:虛引用和沒有引用一樣,必須配合引用佇列使用。

 

我們來看例子:

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; public class Test{ final Map<String,SoftReference> m = new HashMap<>(); public static void main(String args[]){
//建立一個新的弱引用,引用給定的物件 String strong = new String("strong");//strong對"strong"的強引用 String soft = new String("soft");//soft對"soft"的強引用 String weak = new String("weak");//weak對"weak"的強引用 String phantom = new String("phantom");//phantom對"phantom"的強引用 ReferenceQueue<String> queue = new
ReferenceQueue<String>();//引用佇列 Reference<String> reStrong = new SoftReference<>(strong); Reference<String> reSoft = new SoftReference<>(soft);//reSoft對"soft"的軟引用 Reference<String> reWeak = new WeakReference<>(weak);//reWeak對"weak"的弱引用 Reference<String> rePhantom = new PhantomReference<>(phantom,queue);//rePhantom指向"phantom"的虛引用 soft = null;//斷開soft對"soft"的強引用 weak = null;//斷開weak對"weak"的強引用 phantom = null;//斷開phantom對"phantom"的虛引用 //斷開強引用後,就只剩下reSoft對"soft"的軟引用,reWeak對"weak"的弱引用,rePhantom對"phantom"的虛引用 System.out.println("strong執行gc前:"+strong);//強引用物件 System.out.println("soft執行gc前:"+reSoft.get());//獲取當前軟引用的物件 System.out.println("weak執行gc前:"+reWeak.get());//獲取當前弱引用的物件 System.out.println("phantom執行gc前:"+rePhantom.get());//獲取當前虛引用的物件 System.gc();//執行垃圾回收 System.out.println("-----------------------"); System.out.println("strong執行gc後:"+strong);//強引用物件 System.out.println("soft執行gc後:"+reSoft.get());//獲取當前軟引用的物件 System.out.println("weak執行gc後:"+reWeak.get());//獲取當前弱引用的物件 System.out.println("phantom執行gc後:"+rePhantom.get());//獲取當前虛引用的物件 } }

 

執行結果:
strong執行gc前:strong soft執行gc前:soft weak執行gc前:weak phantom執行gc前:
null ----------------------- strong執行gc後:strong soft執行gc後:soft weak執行gc後:null phantom執行gc後:null

我們看上述結果:

強引用在執行垃圾回收後也不會被回收,軟引用在執行垃圾回收後,如果記憶體足夠則不回收,如果記憶體不足則回收。

弱引用在執行垃圾回收後,不論記憶體是否充足都會對其回收。虛引用就可沒有引用一樣,無論何時都會被回收。

 

一般像Date time = new Date();這種都是強引用,也是我們平常使用最多的,只要強引用存在,gc執行後不會對其回收。

我們先看上列程式碼中的軟引用所引用,一開始soft物件對“soft”是一個強引用,然後reSoft對“soft”是一個軟引用,

之後soft物件指向null,即斷開了對“soft”的強引用,此時只剩下reSoft對“soft”的軟引用。

再來分析弱引用,一開始weak對“weak”的強引用,然後是reWeak對“weak”的一個弱引用,

之後weak物件指向nul,即斷開了對“weak”的強引用,此時只剩下reWeak對“weak”的弱引用。

最後後還有一個虛引用phantom。

 

下面畫個圖來看下:

 

還有一點需要說明:

我們先來看下列程式碼:

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class Test{
    final Map<String,SoftReference> m = new HashMap<>();
    public static void main(String args[]){
        String weak = "weak";//weak對"weak"的強引用
        Reference<String> reWeak = new WeakReference<>(weak);//reWeak對"weak"的弱引用
        weak = null;//斷開weak對"weak"的強引用
        System.out.println("weak執行gc前:"+reWeak.get());//獲取當前弱引用的物件
        System.gc();//執行垃圾回收
        System.out.println("-----------------------");
        System.out.println("weak執行gc後:"+reWeak.get());//獲取當前弱引用的物件
    }
}
執行結果:
weak執行gc前:weak
-----------------------
weak執行gc後:weak

看到這個可能會有疑問,不是隻有一個弱引用執行"weak"嗎,執行gc後應該會被回收呀?

大家注意new String("weak")和“weak”是不一樣的,前者新建一個物件時是放在堆中,而後者是一個字串常量是放在靜態區的。

而靜態區的物件是不會被清理的,所以即使只有弱引用指向“weak”,但由於“weak”不會被清理,所以弱引用依然指向"weak"。

 

對於需要經常使用的內容我們可以採用軟引用,這樣在需要使用時既不會被回收,也不會出現記憶體溢位(OOM)錯誤,很適合做為快取。

當一些只需要少量使用較大資料時,我們可以採用弱引用,防止其佔用記憶體。

這些可以根據具體使用情況優化引用關係。

這裡有一個別人舉得使用軟引用優化記憶體溢位的例子:

例子出自:https://blog.csdn.net/arui319/article/details

//首先定義一個HashMap,儲存軟引用物件。
private
Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>(); public void addBitmapToCache(String path) { // 強引用的Bitmap物件 Bitmap bitmap = BitmapFactory.decodeFile(path);//此方法被執行完,bitmap會被清理,此時只保留下了imageCache中對 // 軟引用的Bitmap物件                 //BitmapFactory.decodeFile(path)的軟引用,要是實在不放心就對bitmap置null.             SoftReference<Bitmap> softBitmap = new SoftReference<Bitmap>(bitmap); // 將軟引用物件放到到Map中使其快取 imageCache.put(path, softBitmap); } public Bitmap getBitmapByPath(String path) { // 從Map中取軟引用的value(Bitmap)物件 SoftReference<Bitmap> softBitmap = imageCache.get(path); // 判斷當前軟引用物件是否被清理 if (softBitmap == null) { return null; } // 如果被清理返回null,反之返回改物件 Bitmap bitmap = softBitmap.get(); return bitmap; }

 

二.WeakHashMap

理解了上列內容,就很容易理解WeakHashMap了。

WeakHashMap的鍵是弱引用,回收鍵後刪除key-value物件。

import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;

public class TestWeakHashMap {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Map<String ,String> m = new WeakHashMap<>();
        m.put(new String("1"), "一");
        m.put(new String("2"), new String("二"));
        m.put("3", new String("三"));
        m.put(new String("4"), new String("四"));
        System.gc();
        Set<Map.Entry<String,String>> s_m = m.entrySet();
        Iterator<Map.Entry<String,String>>ite = s_m.iterator();
        while(ite.hasNext()){
            Map.Entry<String, String> en = ite.next();
            System.out.println("ket:" + en.getKey() + "\t" + "value:" + en.getValue());
        }
    }
}
執行結果:
ket:3    value:三

執行gc後弱引用的鍵都被回收了,但“3”在靜態區不會被回收,所以弱引用仍然可以指向“三”。

 

三、IdentityHashMap

IdentityHashMap是比較鍵的地址去重,如果鍵的地址相同就代表同一個物件,如果鍵的地址不同就表示不同物件。

 而HashMap是使用hashCode和equals去重。

import java.util.IdentityHashMap;
import java.util.Map;

public class TestIdentityHashMap {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Map<String,String> m = new IdentityHashMap<>();
        m.put("1", "1");//保留
        m.put("1", "1");//去除
        m.put(new String("1"), "1");//保留
        m.put(new String("1"), "1");//保留
        System.out.println(m.size());
    }

}
執行結果:
3

第一個“1”是字串常量,存放在靜態區是共享的,第一個和第二個“1”都是指向同一個存放在靜態區的常量“1”。所以第二個放置時地址相同會被捨棄。

new String("1"),是在堆中開闢一塊存放“1”的地址,無論該值是否存在,只要new了就會開闢一個新的空間。

我們來看一個圖:

 

四.EnumMap

鍵(key)必須為列舉的值,構造方法中必須為指定列舉類。

import java.util.EnumMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class TestEnumMap {
    public static void main(String args[]){
        Map<Season,String> m = new EnumMap<>(Season.class);//構造方法指定列舉類
        m.put(Season.SPRING, "春困");
        m.put(Season.SUMMER, "夏乏");
        m.put(Season.AUTUMN, "秋無力");
        m.put(Season.WINTER, "冬日正好眠");
        Set<Entry<Season, String>> s_m = m.entrySet();
        Iterator<Entry<Season, String>>ite = s_m.iterator();
        while(ite.hasNext()){
            Entry<Season, String> en = ite.next();
            System.out.println(en.getValue());
        }
    }
}

enum Season{
    SPRING,SUMMER,AUTUMN,WINTER
}
執行結果:
春困
夏乏
秋無力
冬日正好眠