程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 解析WeakHashMap與HashMap的差別詳解

解析WeakHashMap與HashMap的差別詳解

編輯:關於JAVA

解析WeakHashMap與HashMap的差別詳解。本站提示廣大學習愛好者:(解析WeakHashMap與HashMap的差別詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是解析WeakHashMap與HashMap的差別詳解正文


WeakHashMap,此種Map的特色是,當除本身有對key的援用外,此key沒有其他援用那末此map會主動拋棄此值,
見實例:此例子中聲清楚明了兩個Map對象,一個是HashMap,一個是WeakHashMap,同時向兩個map中放入a、b兩個對象,當HashMap  remove失落a 而且將a、b都指向null時,WeakHashMap中的a將主動被收受接管失落。湧現這個狀態的緣由是,關於a對象而言,當HashMap  remove失落而且將a指向null後,除WeakHashMap中還保留a外曾經沒有指向a的指針了,所以WeakHashMap會主動捨棄失落a,而關於b對象固然指向了null,但HashMap中還有指向b的指針,所以
WeakHashMap將會保存

package test; 

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

public class Test { 
    public static void main(String[] args) throws Exception { 
        String a = new String("a"); 
        String b = new String("b"); 
        Map weakmap = new WeakHashMap(); 
        Map map = new HashMap(); 
        map.put(a, "aaa"); 
        map.put(b, "bbb"); 

         
        weakmap.put(a, "aaa"); 
        weakmap.put(b, "bbb"); 

        map.remove(a); 

        a=null; 
        b=null; 

        System.gc(); 
        Iterator i = map.entrySet().iterator(); 
        while (i.hasNext()) { 
            Map.Entry en = (Map.Entry)i.next(); 
            System.out.println("map:"+en.getKey()+":"+en.getValue()); 
        } 

        Iterator j = weakmap.entrySet().iterator(); 
        while (j.hasNext()) { 
            Map.Entry en = (Map.Entry)j.next(); 
            System.out.println("weakmap:"+en.getKey()+":"+en.getValue()); 

        } 
    } 

     


先把成績說清晰:
WeakHashMap是重要經由過程expungeStaleEntries這個函數的來完成移除其外部不消的條目從而到達的主動釋放內存的目標的.根本上只需對WeakHashMap的內容停止拜訪就會挪用這個函數,從而到達消除其外部不在為內部援用的條目。然則假如事後生成了WeakHashMap,而在GC之前又不曾拜訪該WeakHashMap,那不是就不克不及釋放內存了嗎?
對應的兩個測試案例:
WeakHashMapTest1:

public class WeakHashMapTest1 {
public static void main(String[] args) throws Exception {
List<WeakHashMap<byte[][], byte[][]>> maps = new ArrayList<WeakHashMap<byte[][], byte[][]>>();
for (int i = 0; i < 1000; i++) {
WeakHashMap<byte[][], byte[][]> d = new WeakHashMap<byte[][], byte[][]>();
d.put(new byte[1000][1000], new byte[1000][1000]);
maps.add(d);
System.gc();
System.err.println(i);}}}

因為Java默許內存是64M,所以再不轉變內存參數的情形下,該測試跑不了幾步輪回就內存溢出了。果不其然,WeakHashMap這個時刻並沒有主動幫我們釋放不消的內存。
WeakHashMapTest2:

public class WeakHashMapTest2 {
public static void main(String[] args) throws Exception {
List<WeakHashMap<byte[][], byte[][]>> maps = new ArrayList<WeakHashMap<byte[][], byte[][]>>();
for (int i = 0; i < 1000; i++) {
WeakHashMap<byte[][], byte[][]> d = new WeakHashMap<byte[][], byte[][]>();
d.put(new byte[1000][1000], new byte[1000][1000]);
maps.add(d);
System.gc();
System.err.println(i);
for (int j = 0; j < i; j++) {
System.err.println(j + " size" + maps.get(j).size());
}
}
}
}

此次測試輸入正常,不在湧現內存溢出成績.
總結來講:WeakHashMap其實不是你啥也干他就可以主動釋放外部不消的對象的,而是在你拜訪它的內容的時刻釋放外部不消的對象
成績講清晰了,如今我們來梳理一下.懂得清晰個中的奧妙.
WeakHashMap完成弱援用,是由於它的Entry<K,V>是繼續自WeakReference<K>的
在WeakHashMap$Entry<K,V>的類界說及結構函數外面是如許寫的:

private static class Entry<K,V>
extends WeakReference<K>
implements Map.Entry<K,V> Entry(K key, V value, ReferenceQueue<K> queue,int hash, Entry<K,V> next) {
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}

請留意它結構父類的語句:“super(key, queue);”,傳入的是key,是以key才是停止弱援用的,value是直接強援用聯系關系在this.value當中.在System.gc()時,key中的byte數組停止了收受接管,而value仍然堅持(value被強聯系關系到entry上,entry又聯系關系在map中,map聯系關系在arrayList中.).
若何證實key中的byte被收受接管了呢?可以經由過程內存溢出時導出的內存鏡像停止剖析,也能夠經由過程以下的小測試得出結論:
把下面的value用小對象取代,

for (int i = 0; i < 10000; i++) {
WeakHashMap<byte[][], Object> d = new WeakHashMap<byte[][], Object>();
d.put(new byte[1000][1000], new Object());
maps.add(d); System.gc();
System.err.println(i);
}

下面的代碼,即便履行10000次也沒有成績,證實key中的byte數組確切被收受接管了。
for輪回中每次都new一個新的WeakHashMap,在put操作後,固然GC將WeakReference的key中的byte數組收受接管了,並將事宜告訴到了ReferenceQueue,但後續卻沒有響應的舉措去觸發 WeakHashMap 行止理 ReferenceQueue
所以 WeakReference 包裝的key仍然存在在WeakHashMap中,其對應的value也固然存在。
 那value是什麼時候被消除的呢?
對兩個例子停止剖析可知,例子二中的maps.get(j).size()觸發了value的收受接管,那又若何觸發的呢.檢查WeakHashMap源碼可知,size辦法挪用了expungeStaleEntries辦法,該辦法對vm要收受接管的的entry(quene中)停止遍歷,並將entry的value置空,收受接管了內存.
所以後果是key在GC的時刻被消除,value在key消除後拜訪WeakHashMap被消除.
疑問:key的quene與map的quene是統一個quene,poll操作會削減一個reference,那成績是key假如先被消除,expungeStaleEntries遍歷quene時誰人被收受接管的key對應的entry還能掏出來麼???
關於履行System.GC時,key中的byte數據若何被收受接管了,請見WeakReference referenceQuene
WeakHashMap
public class WeakHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>
以弱鍵完成的基於哈希表的 Map。在 WeakHashMap 中,當某個鍵不再正常應用時,將主動移除其條目。
更准確地說,關於一個給定的鍵,其映照的存在其實不阻攔渣滓收受接管器對該鍵的拋棄,這就使該鍵成為可終止的,被終止,然後被收受接管。
拋棄某個鍵時,其條目從映照中有用地移除,是以,該類的行動與其他的 Map 完成有所分歧。
null 值和 null 鍵都被支撐。該類具有與 HashMap 類類似的機能特點,並具有雷同的效能參數初始容量 和加載因子。
像年夜多半聚集類一樣,該類是分歧步的。可使用 Collections.synchronizedMap 辦法來結構同步的 WeakHashMap。
該類重要與如許的鍵對象一路應用,其 equals 辦法應用 == 運算符來測試對象標識。
一旦這類鍵被拋棄,就永久沒法再創立了,所以,過段時光後在 WeakHashMap 中查找此鍵是弗成能的,不用對其項已移除而覺得驚奇。
該類非常合適與 equals 辦法不是基於對象標識的鍵對象一路應用,好比,String 實例。
但是,關於這類可從新創立的鍵對象,鍵若拋棄,就主動移除 WeakHashMap 條目,這類表示使人困惑。
WeakHashMap 類的行動部門取決於渣滓收受接管器的舉措,所以,幾個罕見的(固然不是必須的)Map 常量不支撐此類。
由於渣滓收受接管器在任什麼時候候都能夠拋棄鍵,WeakHashMap 就像是一個被靜靜移除條目標未知線程。
特殊地,即便對 WeakHashMap 實例停止同步,而且沒有挪用任何賦值辦法,在一段時光後 ,size 辦法也能夠前往較小的值,
關於 isEmpty 辦法,能夠前往 false,然後前往 true,關於給定的鍵,containsKey 辦法能夠前往 true 然後前往 false,關於給定的鍵,
get 辦法能夠前往一個值,但接著前往 null,關於之前湧現在映照中的鍵,put 辦法前往 null,而 remove 辦法前往 false,
關於鍵集、值集、項集停止的檢討,生成的元素數目愈來愈少。
WeakHashMap 中的每一個鍵對象直接地存儲為一個弱援用的指導對象。是以,不論是在映照內照樣在映照以外,
只要在渣滓收受接管器消除某個鍵的弱援用以後,該鍵才會主動移除。
完成留意事項:WeakHashMap 中的值對象由通俗的強援用堅持。是以應當當心謹嚴,確保值對象不會直接或直接地強援用其本身的鍵,
由於這會阻攔鍵的拋棄。留意,值對象可以經由過程 WeakHashMap 自己直接援用其對應的鍵;
這就是說,某個值對象能夠強援用某個其他的鍵對象,而與該鍵對象相干聯的值對象轉而強援用第一個值對象的鍵。
處置此成績的一種辦法是,在拔出前將值本身包裝在 WeakReferences 中,如:m.put(key, new WeakReference(value)),
然後,分離用 get 停止解包。
該類一切“collection 視圖辦法”前往的迭代器均是疾速掉敗的:在迭代器創立以後,
假如從構造上對映照停止修正,除非經由過程迭代器本身的 remove 或 add 辦法,其他任什麼時候間任何方法的修正,
迭代器都將拋出 ConcurrentModificationException。是以,面臨並發的修正,迭代器很快就完整掉敗,
而不是冒著在未來不肯定的時光隨意率性產生不肯定行動的風險。
留意,迭代器的疾速掉劣行為不克不及獲得包管,普通來講,存在分歧步的並發修正時,弗成能作出任何果斷的包管。
疾速掉敗迭代器盡最年夜盡力拋出 ConcurrentModificationException。是以,編寫依附於此異常法式的方法是毛病的,
准確做法是:迭代器的疾速掉劣行為應當僅用於檢測 bug。
留意1:null 值和 null 鍵都被支撐。
留意2:不是線程平安的。
留意3:迭代器的疾速掉劣行為不克不及獲得包管。
留意4:WeakHashMap是無序的。
留意5:確保值對象不會直接或直接地強援用其本身的鍵,
由於這會阻攔鍵的拋棄。然則,值對象可以經由過程 WeakHashMap 自己直接援用其對應的鍵;
這就是說,某個值對象能夠強援用某個其他的鍵對象,而與該鍵對象相干聯的值對象轉而強援用第一個值對象的鍵,這時候就構成了環路。
處置此成績的一種辦法是,在拔出前將值本身包裝在WeakReferences中,如:m.put(key, new WeakReference(value)),
然後,分離用 get 停止解包。照實例1.
實例1:

import java.lang.ref.WeakReference;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
public class Test {
 /**
  * @param args
  */
 public static void main(String[] args) {
  WeakHashMap<Integer,WeakReference<People>> map=new WeakHashMap<Integer,WeakReference<People>>();
  People p1=new People("robin",1,28);
  People p2=new People("robin",2,29);
  People p3=new People("harry",3,30);
  map.put(new Integer(100), new WeakReference<People>(p1));
  map.put(new Integer(101), new WeakReference<People>(p2));
  map.put(new Integer(1), new WeakReference<People>(p3));

  for(WeakReference<People> rp:map.values())
  {
   People p= rp.get();
   System.out.println("people:"+p);
  }
 }
}
class People{
 String name;
 int id;
 int age;
 public People(String name,int id)
 {
  this(name,id,0);
 }
 public People(String name,int id,int age)
 {
  this.name=name;
  this.id=id;
  this.age=age;
 }
 public String toString()
 {
  return id+name+age;
 }
 public boolean equals(Object o)
 {
  if(o==null)
   return false;
  if(!(o instanceof People))
   return false;
  People p=(People)o;
  boolean res=name.equals(p.name);
  if(res)
   System.out.println("name "+name+" is double");
  else
   System.out.println(name+" vS "+p.name);
  return res;
 }
 public int hashCode()
 {
  return name.hashCode();
 }
}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved