目錄java
序:使用java的Map作緩存,你是否考慮過容量致使的OOM問題,是否考慮命中率對性能的影響??mysql
應用系統開發中,咱們常常會使用redis,memcache等第三方框架作緩存的解決方案,有的時候咱們的需求以及應用場景並非那麼複雜,並且交付日期已經秒計了。咱們怎麼敢在現有的應用中引入第三方框架,火都把頭頂燒禿咯。這個時候怎麼辦,績效啊,年終獎啊。redis
可能大部分人使用緩存都僅僅是取和存操做,可是呢!若是對計算機操做系統有所瞭解,其實不用看redis的配置文件就知道要考慮容量的問題。好比操做系統中的頁面調度的各類FIFO,LRU算法都是爲了提升命中率。一樣咱們在應用中使用緩存也應該考慮命中率和容量問題。尤爲是咱們使用Java的map作簡單的緩存,更是應該考慮。算法
女神:容量是嗎?說的那麼高大上,不就new的時候指定一下容量嘛,這麼簡單sql
new HashMap<Integer, Byte[]>(100);
intsmaze:您好,你能夠嘗試往map裏面添加101個元素,而後遍歷map看看遍歷的數據個數是100仍是101,HashMap內部有一個數組,會自動擴容的親。api
女神:哎呀,確實。那麼我就把HashMap封裝一下吧。數組
private Map<String,String> map=new HashMap<String,String>(); private int len; public intsmaze(int len) { this.len=len; } boolean put(String key,String value) { if(map.size()==len) { return false; } else { map.put(key, value); return true; } }
intsmaze:您好,這樣確實解決了OOM問題,可是我有一個問題不知當講不當講,這樣作的話,是否是put len次後,後面的數據都不會存儲了,get的時候永遠只能從get到錢len次的數據,其餘的數據要走硬盤去讀了?緩存
女神:是的,我把len的大小設置大一點,而後每隔一個小時清空一下map裏面的值,不就好了,你爲何要針對我啊?安全
intsmaze: 額,你這樣也能夠,可是要在前期花時間調試Map大小,選擇一個合適的大小,並且每隔一個小時,mysql等存儲都會面臨大量的請求,容易引起緩存雪崩。並且若是最求性能的話,這裏其實仍是有提升的,命中率的高低決定了性能的高低。多線程
女神: 那你想怎麼樣?咋滴啥?
intsmaze: 這個時候你能夠了解一下FIFO,LRU等。若是你用過redis,你應該知道,不你可能知道,redis關於命中率三種策略(FIFO 、LRU、LFU)。因此咱們若是要使用Map作緩存,咱們也應該考慮一下命中率。後面編不下去了,直接講這篇文章的重點吧。
WeakHashMap實現了Map接口,使用弱引用做爲內部數據的存儲方案。WeakHashMap是弱引用的典型應用,能夠做爲簡單的緩存表解決方案。WeakHashMap會在系統內存範圍內,保存全部表項目,一旦內存不夠,在GC時,沒有被引用的表項很快會被清除掉,從而避免系統內存溢出。
關於弱引用我就不講啦,百度一大堆
Map<Integer, Byte[]> map = null; map = new WeakHashMap<Integer, Byte[]>(); for (int i = 0; i < 10000; i++) { Integer integer = new Integer(i); map.put(integer, new Byte[i]); } //-Xmx5M 這個時候發現沒有OOM // -Xmx5M java.lang.OutOfMemoryError: Java heap space map = new HashMap<Integer, Byte[]>(10); for (int i = 0; i < 100; i++) { Integer integer = new Integer(i); map.put(integer, new Byte[i]); } //若是存放在WeakHashMap中的key都存在強引用,那麼WeakHashMap就會退化爲HashMap。 // -Xmx5M java.lang.OutOfMemoryError: Java heap space // at cn.intsmaze.collection.MapCase.testWeakHash(MapCase.java:119) map = new WeakHashMap<Integer, Byte[]>(); List list = new ArrayList(); for (int i = 0; i < 10000; i++) { Integer integer = new Integer(i); map.put(integer, new Byte[i]);// 若是你看不起我,你能夠把這行註釋,你將會發現姜仍是老的辣,內存溢出是WeakHashMap而不是List致使. list.add(integer); }
若是但願在系統中經過WeakHashMap自動清理數據,儘可能不要在系統的其餘地方強引用WeakHashMap的key,不然,這些key就不會被回收,WeakHashMap也就沒法正常釋放他們所佔用的表項。
前面已經知道,使用WeakHashMap能夠忽略容量問題,提高緩存容量。只是當容量不夠時,不會OOM,內部數據會被GC回收。命中率好像沒有辦法,容我掉一片頭髮換來深度思考後給出方案。
使用WeakHashMap通常是全局變量,局部變量的應用場景應該沒有吧。
觀察WeakHashMap源碼能夠發現,它是線程不安全的,因此在多線程場景該怎麼辦嘞?
WeakHashMap<String, String> weakHashMapintsmaze=new WeakHashMap<String, String>(); Map<String, String> intsmaze=Collections.synchronizedMap(weakHashMapintsmaze);
就問你服不服。
一個ThreadLocal記錄一個weakHashMap,良好的系統是不會不斷的建立銷燬線程的,而是有線程池進行維護,那麼就用ThreadLocal吧。不懂,你能夠先關注我,再去百度。
個人博客即將搬運同步至騰訊雲+社區,邀請你們一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=2mx7vvis5a80k