内存缓存LruCache算法

内存缓存技术LruCache原理分析


1.告别软引用

最近几天在学习缓存技术,缓存无非就是利用空间换时间,以达到应用流畅的目的。在三级缓存中,最重要的就是内存缓存,因为cpu与内存的直接读取关系,我们可以让图片等数据保存在内存中,保证用户下次读取不用无聊的等待。同时,内存又是很珍贵的空间,所以我们不能无限制的使用内存。

一般情况下,我们都会想到使用软引用或者弱引用,比如利用HashMap以键值对的方式存储经软引用封装的Bitmap对象:

private HashMap<String,SoftReference<Bitmap>> mCache = new HashMap<String, SoftReference<Bitmap>>();

不同于强引用在系统内存不足时垃圾回收器不会进行回收,软引用在系统内存不足时垃圾回收器会考虑回收。

然而,现在这种方式并不推荐:
在Android2.x+(忘了具体哪个版本 TvT)之后, Android在内存够用时也会进行软引用对象的回收,所以这种方式就不管用了。

2.最近最少原则

Android 3.0 (API Level 11)中,图片的数据会存储在本地的内存当中,因而无法用一种可预见的方式将其释放,这就有潜在的风险造成应用程序的内存溢出并崩溃,这时就可以用LruCache来缓存图片。
Lru = Least recently used(最近最少使用),开辟一块固定大小的内存,把图片存放在内存中,当图片对象越来越多,内存快装满的时候,在一推缓存好的图片中把最近最少使用的图片给剔除,这样就有个好处,通过动态的剔除图片控制了缓存空间大小,缓存的同时不会造成内存溢出。

3.LinkedHashMap分析

public class LruCache<K, V> {
    private final LinkedHashMap<K, V> map;

    /** Size of this cache in units. Not necessarily the number of elements. */
    private int size;//已经存储的大小
    private int maxSize;//规定的最大存储空间

    private int putCount;
    private int createCount;
    private int evictionCount;
    private int hitCount;
    private int missCount;

LruCache的内部定义了一个LinkedHashMap,这个LinkedHashMap本质上也是个HashMap,那有人会问了,为什么不用HashMap而用LinkedHashMap?

解释下上面的问题,LinkedHashMap是比HashMap多了一个链表的结构。与HashMap相比LinkedHashMap维护的是一个具有双重链表的HashMap,LinkedHashMap支持2中排序一种是插入排序,一种是使用排序,最近使用的会移至尾部例如 M1 M2 M3 M4,使用M3后为 M1 M2 M4 M3了,LinkedHashMap输出时其元素是有顺序的,所以利用这一特性,我们就可以实现把最近最多使用的图片对象放在尾部,最少使用的就自然而然到了顶部,内存不足时剔除就行。而HashMap输出时是随机的,如果Map映射比较复杂而又要求高效率的话,最好使用LinkedHashMap,但是多线程访问的话可能会造成不同步,所以要用Collections.synchronizedMap来包装一下,从而实现同步。

HashMap是java的数据结构,而LinkedHashMap是Android特有的数据结构,在开发中尽量使用Android特有的数据结构,比如SparseArray和LinkedHashMap等等,他们都有更好的性能。

4.重写sizeof

LruCache已经封装在官方api下了,我们使用v4包下的LruCache:

    //开辟的缓存大小最好为应用可用内存大小的1/8
    int maxSize = (int) (Runtime.getRuntime().maxMemory() / 8);
    //传入一个参数为缓存空间的总大小,和两个泛型为键值对,值为图片对象
    LruCache mCache = new LruCache<String, Bitmap>(maxSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return (value.getRowBytes() * value.getHeight());
            }
        };

使用LruCache要重写里面的sizeof方法,返回的是一个图片能占多大内存,图片占内存=图片的行字节×高(value.getRowBytes() * value.getHeight()),默认是1。

5.trimToSize

LruCache有put和get两个公共方法以键值对的方式存取对象到LinkedHashMap里,存取的同时size += safeSizeOf(key, value);和size -= safeSizeOf(key, previous);控制size的大小

private void trimToSize(int maxSize) {
        while (true) {
            K key;
            V value;
            synchronized (this) {
                if (size < 0 || (map.isEmpty() && size != 0)) {
                    throw new IllegalStateException(getClass().getName()
                            + ".sizeOf() is reporting inconsistent results!");
                }

                if (size <= maxSize) {
                    break;
                }

                // BEGIN LAYOUTLIB CHANGE
                // get the last item in the linked list.
                // This is not efficient, the goal here is to minimize the changes
                // compared to the platform version.
                Map.Entry<K, V> toEvict = null;
                for (Map.Entry<K, V> entry : map.entrySet()) {
                    toEvict = entry;
                }
                // END LAYOUTLIB CHANGE

                if (toEvict == null) {
                    break;
                }

                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);
                size -= safeSizeOf(key, value);
                evictionCount++;
            }

            entryRemoved(true, key, value, null);
        }
    }

这个方法是Lru的核心,put的时候,会判断当前总大小size是否小于开发者定的maxSize,如果小于就break循环完事,如果大于的话,就remove那个map中最近最少使用对象,再把当前size减少。

总结:
通过这个size(当前对象集合所使用的内存大小)控制一旦发现size大于开发者定的maxsize的话,删除一个对象,而这个对象是遍历出来的第一个(这个就是最近最少使用的一个),通过while循环,如果可用size还不够的话就接着删除下一个对象。通过这样的方式控制size的大小总是小于maxsize,从而达到控制内存大小。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值