java使用Map做緩存你真的用對了嗎?弱引用WeakHashMap了解一下


序:使用java的Map做緩存,你是否考慮過容量導致的OOM問題,是否考慮命中率對性能的影響??


應用系統開發中,我們經常會使用redis,memcache等第三方框架做緩存的解決方案,有的時候我們的需求以及應用場景並不是那么復雜,而且交付日期已經秒計了。我們怎么敢在現有的應用中引入第三方框架,火都把頭頂燒禿咯。這個時候怎么辦,績效啊,年終獎啊。

關於緩存我們應該考慮什么?-intsmaze

可能大部分人使用緩存都僅僅是取和存操作,但是呢!如果對計算機操作系統有所了解,其實不用看redis的配置文件就知道要考慮容量的問題。比如操作系統中的頁面調度的各種FIFO,LRU算法都是為了提高命中率。同樣我們在應用中使用緩存也應該考慮命中率和容量問題。尤其是我們使用Java的map做簡單的緩存,更是應該考慮。

女神:容量是嗎?說的那么高大上,不就new的時候指定一下容量嘛,這么簡單

new HashMap<Integer, Byte[]>(100);

intsmaze:您好,你可以嘗試往map里面添加101個元素,然后遍歷map看看遍歷的數據個數是100還是101,HashMap內部有一個數組,會自動擴容的親。

女神:哎呀,確實。那么我就把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弱引用-intsmaze

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也就無法正常釋放他們所占用的表項。

線程安全問題-intsmaze

前面已經知道,使用WeakHashMap可以忽略容量問題,提升緩存容量。只是當容量不夠時,不會OOM,內部數據會被GC回收。命中率好像沒有辦法,容我掉一片頭發換來深度思考后給出方案。
使用WeakHashMap一般是全局變量,局部變量的應用場景應該沒有吧。
觀察WeakHashMap源碼可以發現,它是線程不安全的,所以在多線程場景該怎么辦嘞?

Collections-intsmaze

WeakHashMap<String, String> weakHashMapintsmaze=new WeakHashMap<String, String>();
Map<String, String> intsmaze=Collections.synchronizedMap(weakHashMapintsmaze);

就問你服不服。

ThreadLocal-intsmaze

一個ThreadLocal記錄一個weakHashMap,良好的系統是不會不斷的創建銷毀線程的,而是有線程池進行維護,那么就用ThreadLocal吧。不懂,你可以先關注我,再去百度。

我的博客即將搬運同步至騰訊雲+社區,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=2mx7vvis5a80k


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM