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 = newReferenceQueue<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 }
執行結果:
春困
夏乏
秋無力
冬日正好眠