LruCache使用原理

上一篇:

LinkedHashMap jdk1.8基础和源码分析

LruCache缓存的核心:LRU(Least Recently Used)最近最少使用算法,即当缓存快要满时,会优先淘汰那些近期最少使用的缓存对象。

LruCache的核心是LinkedHashMap,LinkedHashMap的accessOrder为true,表示按访问顺序进行迭代;建议先看LinkedHashMap jdk1.8基础和源码分析,再来看LruCche就很简单啦。

Android 3.0之前做图片缓存主要用的就是SoftReference,3.0以后虚拟机更倾向于用SoftReference来索引对象,所以LruCache的出现就是为了取代它。


四种引用:

强引用:使用new关键字创建的就是强引用。当一个对象具有强引用,那么垃圾回收器是绝对不会的回收和销毁它的。

软引用:如果一个对象只具有软引用,若内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,才会回收这些对象的内存。但是系统并不知道哪些软引用指向的对象应该被回收,哪些应该被保留。过早地回收对象会导致不必要的工作。

弱引用:在垃圾回收器运行的时候,如果对一个对象的所有引用都是弱引用的话,该对象会被回收。

虚引用:一个只被虚引用持有的对象可能会在任何时候被GC回收。虚引用对对象的生存周期完全没有影响,也无法通过虚引用来获取对象实例,仅仅能在对象被回收时,得到一个系统通知(只能通过是否被加入到ReferenceQueue来判断是否被GC,这也是唯一判断对象是否被GC的途径)。

有点乏味,看源码之前先插个图:

LruCache缓存的插入、查询、删除操作源码解析:

LruCache<K,V>构造函数

public class LruCache<K, V> {
    private final LinkedHashMap<K, V> map;
    private int size; 
    private int maxSize;
    
    private int putCount; 
    private int createCount;
    private int evictionCount;
    private int hitCount; 
    private int missCount;

    public LruCache(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        this.maxSize = maxSize; //最多可存储多少个element,构造函数中赋值
        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
    }
}
复制代码

put()插入操作

putCount++,如果map中已经存在key,则返回oldValue,否则返回null;并判断是否需要移除最近最少访问的对象

public final V put(K key, V value) {
        if (key == null || value == null) { //不允许空键和空值,LinkedHashMap默认是允许一个空键和N个空值的
            throw new NullPointerException("key == null || value == null");
        }
        V previous;
        synchronized (this) { 
            putCount++; //putCount加1
            size += safeSizeOf(key, value); //size加上element占用的空间
            previous = map.put(key, value); //linkedHashMap.put操作
            if (previous != null) { //如果map中已经存在key,只是更新value,则减去oldElement占用的空间
                size -= safeSizeOf(key, previous);
            }
        }
        if (previous != null) {
            entryRemoved(false, key, previous, value); //空方法
        }
        trimToSize(maxSize);
        return previous;
    }
    
    //size必须大于0
    private int safeSizeOf(K key, V value) {
        int result = sizeOf(key, value);
        if (result < 0) {
            throw new IllegalStateException("Negative size: " + key + "=" + value);
        }
        return result;
    }
    
    //默认实现返回1,用户可自定义
     protected int sizeOf(K key, V value) {
        return 1;
    }
    
    protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
复制代码

trimToSize(maxSize)

在缓存将要满之前,删除最近最少访问的对象,即移除掉map的表头元素

 public 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; //如果size<=maxSize,不需要扩容,跳出循环
                }

                Map.Entry<K, V> toEvict = map.eldest(); //取出map的表头head
                if (toEvict == null) {
                    break; //head == null, 无需扩容
                }

                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key); //将表头移除掉,linkedHashMap的特性,最近最少访问的对象存储在表头
                size -= safeSizeOf(key, value); //size修改
                evictionCount++;
            }

            entryRemoved(true, key, value, null);
        }
    }
复制代码

get(K key)

如果能找到key所对应的value, 则返回value,hitCount++;否则missCount++,返回null

public final V get(K key) {
        if (key == null) { //不允许空键
            throw new NullPointerException("key == null");
        }
        V mapValue;
        synchronized (this) {
            mapValue = map.get(key);
            if (mapValue != null) {
                hitCount++;
                return mapValue; //如果能找到key所对应的value, 则返回value,hitCount++;否则missCount++,返回null
            }
            missCount++;
        }
        V createdValue = create(key); //create(key)默认返回一个null
        if (createdValue == null) {
            return null;
        }
    /** 省略掉部分代码 **/
    }
    
    protected V create(K key) {
        return null;
    }
复制代码

remove(K key)删除操作

  public final V remove(K key) {
        if (key == null) { //不允许空键
            throw new NullPointerException("key == null");
        }
        V previous;
        synchronized (this) {
            previous = map.remove(key);
            if (previous != null) { //如果存在key,则返回value,更新size;否则没有找到匹配的key,则返回null
                size -= safeSizeOf(key, previous);
            }
        }
        if (previous != null) {
            entryRemoved(false, key, previous, null); //空方法
        }
        return previous;
    }
复制代码

转载于:https://juejin.im/post/5b51a8485188251aad210deb

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值