Moosphan / Android-Daily-Interview

:pushpin:每工作日更新一道 Android 面试题,小聚成河,大聚成江,共勉之~

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

2019-06-19:简要说说 LruCache 的原理?

Moosphan opened this issue · comments

2019-06-19:简要说说 LruCache 的原理?

LruCache是使用一个LinkedHashMap简单内存的缓存,是强引用;没有软引用。如果添加的数据大于设置的数据,就删除最先缓存数据来调整内存数据。
maxside数据是初始化的时候构造函数设置最大值。表示能够缓存最大数据。

LRU 顾名思义就是最近最少使用,才用链表实现

LruCache是最近最少使用算法
是用LinkedHashMap实现的简单的内存的缓存,是强引用。
如果添加的数据大于设置的最大的缓存数据,就删除最先缓存的或不使用的数据来调整内存。

LruCache 非常适合用于 缓存图片,他的主要算法原理是包最近使用的对象 存储在 LinckHashMap中,并且把最近使用的最少的对象在 缓存值达到预设值之前从内存中移除

`public class LruCachePhoto {
/**
* 图片 缓存技术的核心类,用于缓存下载好的所有图片,
* 在程序内存达到设定值后会将最少最近使用的图片移除掉
*/
private LruCache<String, Bitmap> mMenoryCache;

public LruCachePhoto() {
    //获取应用最大可用内存
    int maxMemory = (int) Runtime.getRuntime().maxMemory();
    //设置 缓存文件大小为 程序最大可用内存的 1/8
    int cacheSize = maxMemory / 8;

    mMenoryCache = new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap value) {
            return value.getByteCount();
        }
    };
}

/**
 * 从 LruCache 中获取一张图片,如果不存在 就返回 null
 *
 * @param key LurCache 的键,这里是 图片的地址
 * @return 返回对应的 Bitmap对象,找不到则为 null
 */
public Bitmap getBitmapFromMemoryCache(String key) {
    return mMenoryCache.get(key);
}

/**
 * 添加一张图片
 *
 * @param key    key
 * @param bitmap bitmap
 */
public void addBitmapToCache(String key, Bitmap bitmap) {
    if (getBitmapFromMemoryCache(key) == null) {
        mMenoryCache.put(key, bitmap);
    }
}

}`

androidx.collection.LruCache
初始化的时候, 会限制缓存占据内存空间的总容量 maxSize;
底层维护的是 LinkedHashMap, 使用 LruCache 最好要重写 sizeOf 方法, 用于计算每个被缓存的对象, 在内存中存储时, 占用多少空间;

在 put 操作时, 首先计算新的缓存对象, 占多少空间, 再根据 key, 移除老的对象,
占用内存大小 = 之前占用的内存大小 + 新对象的大小 - 老对象的大小;
put 操作最后总会根据 maxSize, 在拿到 LinkedHashMap.EntrySet 中链表的头节点, 循环判断, 只要当前缓存对象占据内存超出 maxSize, 就移除一个头节点, 一直到符合要求;

lruCache 和 LinkedHashMap 的关系:
LinkedHashMap 中维护一个双向链表, 并维护 head 和 tail 指针, lruCache 使用了 LinkedHashMap accessOrder 为 true 的属性, 只要访问了某个 key,
包括 get 和 put, 就把当前这个 entry 放在链表的位节点, 所以链表的头节点, 是最老访问的节点, 尾节点是最新访问的节点,
所以, lruCache 就很巧妙的利用了这个特点, 完成了 Least Recently Used 的需求;

LruCache和DiskLruCache底层实现

LRU概念-最近最少使用 将访问的元素根据访问的顺序 进行排序

LruCache和DiskLruCache底层均为LinkedHashMap 是一个由包含KV的结点形成的双向链表

按照进入的顺序排列 当进入的元素在链表里有重复 会将这个元素调到链头表示访问频度高 当超过空间阈值时 会首先淘汰最近最少使用的元素

DiskLru相比于Lru来说 更注重于对文件的读写 和文件操作的保护

LruCache因为其特性会出现在使用中有元素被淘汰 而在业务场景中有时候需要被淘汰的元素 我们可以将其淘汰的元素 存储起来保存在队列中 避免这种情况

LruCache 是最近最少使用算法的一种实现,主要用于内存缓存。

LruCache 内部使用 LinkedHashMap 来实现最近最少使用的核心逻辑。通过构造函数初始化最大的cache 大小,其单位要和 sizeOf 大小一致。还会初始化一个有序的 LinkedHashMap,这个 LinkedHashMap 是个有序的双向链表,根据进入顺序进行排序,新进入或最新使用的放在链表尾部,这样表头就是最少使用的。当超过给定的大小时就会移除表头的数据,知道大小小于给定大小为止。

注意:LruCache 是强引用,LruCache 本身并没有释放内存存,它只是把 LinkedHashMap 中的数据移除,如果数据还在其他地方引用,还是无法释放内存,可以手动释放;

LruCache 是android 提供的缓存类 内部提供了LinkedHashMap来做存储方式 而LinkedHashMap持有对象是以强引用的方式,所以不会被系统回收掉。
在创建的时候给LruCache 指定最大的存储空间 并创建初始量为0的LinkedHashMap来存储对象 重写sizeOf返回存储对象所占用的空间 指定存储空间满了之后 会移动最近最少使用的存储对象

LruCache 是android 提供的缓存类 内部提供了LinkedHashMap来做存储方式 而LinkedHashMap持有对象是以强引用的方式,所以不会被系统回收掉。
在创建的时候给LruCache 指定最大的存储空间 并创建初始量为0的LinkedHashMap来存储对象 重写sizeOf返回存储对象所占用的空间 指定存储空间满了之后 会移动最近最少使用的存储对象