zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    AGC037F Counting of Subarrays
    AGC025F Addition and Andition
    CF506C Mr. Kitayuta vs. Bamboos
    AGC032D Rotation Sort
    ARC101F Robots and Exits
    AGC032E Modulo Pairing
    CF559E Gerald and Path
    CF685C Optimal Point
    聊聊Mysql索引和redis跳表
    什么是线程安全
  • 原文地址:https://www.cnblogs.com/intsmaze/p/9477803.html
Copyright © 2011-2022 走看看