简单聊聊Glide的内存缓存

简介: glide的内存缓存有两级:LruCache、ActiveResources其中LruCache老生常谈了,这里就不细说了。ActiveResources实际上内含一个HashMap,Map中value则是资源的弱引用。那么这两级是如何工作的?

前言


glide的内存缓存有两级:LruCache、ActiveResources

其中LruCache老生常谈了,这里就不细说了。

ActiveResources实际上内含一个HashMap,Map中value则是资源的弱引用。

那么这两级是如何工作的?


取出


先从LruCache取,没有再从ActiveResources取

如果LruCache中有,则取出存入ActiveResources,并从LruCache移除

代码如下:


public class Engine implements EngineJobListener,
        MemoryCache.ResourceRemovedListener,
        EngineResource.ResourceListener {
    ...    
    public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
            DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
            Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
        Util.assertMainThread();
        long startTime = LogTime.getLogTime();
        final String id = fetcher.getId();
        //生成缓存的key
        EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
                loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
                transcoder, loadProvider.getSourceEncoder());
        //从LruCache获取缓存图片
        EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
        if (cached != null) {
            cb.onResourceReady(cached);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Loaded resource from cache", startTime, key);
            }
            return null;
        }
        //从弱引用获取图片
        EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
        if (active != null) {
            cb.onResourceReady(active);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Loaded resource from active resources", startTime, key);
            }
            return null;
        }
        EngineJob current = jobs.get(key);
        if (current != null) {
            current.addCallback(cb);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Added to existing load", startTime, key);
            }
            return new LoadStatus(cb, current);
        }
        EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
        DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
                transcoder, diskCacheProvider, diskCacheStrategy, priority);
        EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
        jobs.put(key, engineJob);
        engineJob.addCallback(cb);
        engineJob.start(runnable);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Started new load", startTime, key);
        }
        return new LoadStatus(cb, engineJob);
    }
    ...
}
复制代码


存储


如果内存本地都没有,则从网络获取,获取后先存入ActiveResources,ActiveResources中存储的是EngineResource对象的弱引用。

EngineResource是将资源进行封装的一个类,它有一个计数acquired,记录资源被引用的次数,当资源被取出使用时+1(acquired函数),当资源被释放时-1(release函数)。当acquired为0时,会将它从ActiveResources中移除,存入LruCache。

代码如下:


void release() {
  synchronized (listener) {
    synchronized (this) {
      if (acquired <= 0) {
        throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
      }
      if (--acquired == 0) {
        listener.onResourceReleased(key, this);
      }
    }
  }
}
复制代码


listener是Engine对象


@Override
public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
  activeResources.deactivate(cacheKey);
  if (resource.isCacheable()) {
    cache.put(cacheKey, resource);
  } else {
    resourceRecycler.recycle(resource);
  }
}
复制代码


可以看到如果开启内存缓存,则存入LruCache,否则直接释放。


两级缓存


这样我们就比较明白glide内存的两级缓存是怎么回事了,实际上是对缓存的资源进行了划分:使用中的和使用过的。

使用中的放入ActiveResources,这样可以防止被LruCache算法回收掉;而使用过的放在LruCache中,通过算法控制内存总量。


release何时执行


上面我们知道当资源被使用时会调用EngineResource的acquired函数,释放的时候会调用EngineResource的release函数。

使用的时候我们比较好理解,取出的时候其实就是使用的时候,这是一个主动的动作。

但是何时释放?glide中是怎么监控资源释放的?

通过查找EngineResource的release函数的调用,找到在Engine中


public void release(Resource<?> resource) {
  if (resource instanceof EngineResource) {
    ((EngineResource<?>) resource).release();
  } else {
    throw new IllegalArgumentException("Cannot release anything but an EngineResource");
  }
}
复制代码


继续查找这个函数在哪里调用,发现在SingleRequest中


@Override
public synchronized void clear() {
  assertNotCallingCallbacks();
  stateVerifier.throwIfRecycled();
  if (status == Status.CLEARED) {
    return;
  }
  cancel();
  // Resource must be released before canNotifyStatusChanged is called.
  if (resource != null) {
    releaseResource(resource);
  }
  if (canNotifyCleared()) {
    target.onLoadCleared(getPlaceholderDrawable());
  }
  status = Status.CLEARED;
  if (toRelease != null) {
    engine.release(toRelease);
  }
}
复制代码


那么这个clear函数又在哪里调用?在ViewTarget中


@Synthetic void pauseMyRequest() {
  Request request = getRequest();
  // If the Request were cleared by the developer, it would be null here. The only way it's
  // present is if the developer hasn't previously cleared this Target.
  if (request != null) {
    isClearedByUs = true;
    request.clear();
    isClearedByUs = false;
  }
}
复制代码


ViewTarget是对要加载图片的ImageView进行封装,而资源的释放也必然与View有关系。

ViewTarget有一个字段protected final T view;这就是要加载图片的ImageView,另外在ViewTarget中可以看到对这个view添加了attach监听:


view.addOnAttachStateChangeListener(attachStateListener);
复制代码


这个attachStateListener的源码:


attachStateListener = new OnAttachStateChangeListener() {
  @Override
  public void onViewAttachedToWindow(View v) {
    resumeMyRequest();
  }
  @Override
  public void onViewDetachedFromWindow(View v) {
    pauseMyRequest();
  }
};
复制代码


这样就很明显了,每个加载图片的view都会注册一个OnAttachStateChangeListener,当这个view从界面移除的时候,也就是资源不再被引用的时候,就会调用pauseMyRequest,最终会将EngineResource的引用计数-1。


这样就保证了当ActiveResources中的资源不再被引用时,将这个资源转移到LruCache中。



目录
相关文章
|
6月前
|
缓存
HOperatorSet.Connection 有内存泄漏或缓存
HOperatorSet.Connection 有内存泄漏或缓存
|
6月前
|
存储 缓存
百度搜索:蓝易云 ,CPU、内存、缓存的关系详细解释!
总结起来,CPU、内存和缓存之间的关系可以概括为:CPU是计算机的处理器,内存是用于存储数据的设备,缓存则是位于CPU和内存之间的高速存储器,用于提高数据的读取速度。它们共同协作,以提供高效的计算机性能。
79 0
|
6月前
|
存储 缓存 Go
Go语言开发者必读:内存缓存技巧
Go语言开发者必读:内存缓存技巧
61 0
|
2月前
|
存储 缓存 算法
Golang高性能内存缓存库BigCache设计与分析
【2月更文挑战第4天】分析Golang高性能内存缓存库BigCache设计
72 0
|
8月前
|
缓存 关系型数据库 MySQL
高性能内存对象缓存Memcached
高性能内存对象缓存Memcached案例
|
5月前
|
存储 缓存 Linux
系统内存管理:虚拟内存、内存分段与分页、页表缓存TLB以及Linux内存管理
虚拟内存的主要作用是提供更大的地址空间,使得每个进程都可以拥有大量的虚拟内存,而不受物理内存大小的限制。此外,虚拟内存还可以提供内存保护和共享的机制,保护每个进程的内存空间不被其他进程非法访问,并允许多个进程共享同一份物理内存数据,提高了系统的资源利用率。虚拟内存的实现方式有分段和分页两种,其中分页机制更为常用和灵活。分页机制将虚拟内存划分为固定大小的页,将每个进程的虚拟地址空间映射到物理内存的页框中。为了减少页表的大小和访问时间,采用了多级页表的方式,将大的页表划分为多个小的页表,只加载需要的页表项,节约了内存空间。
200 0
系统内存管理:虚拟内存、内存分段与分页、页表缓存TLB以及Linux内存管理
|
5月前
|
存储 缓存 Linux
如何在 Linux 中清空缓冲区和缓存内存?
如何在 Linux 中清空缓冲区和缓存内存?
180 0
如何在 Linux 中清空缓冲区和缓存内存?
|
6月前
|
缓存
HOperatorSet.GenRandomRegions 有内存泄漏或缓存,释放不掉
HOperatorSet.GenRandomRegions 有内存泄漏或缓存,释放不掉
|
6月前
|
存储 缓存 NoSQL
Redis第一讲:相关的基础知识/数据类型/缓存的过期策略/双写一致性/内存存储和持久化
Redis第一讲:相关的基础知识/数据类型/缓存的过期策略/双写一致性/内存存储和持久化
|
8月前
|
存储 缓存
【JMM内存模型-4】JMM内存模型之CPU缓存策略
【JMM内存模型-4】JMM内存模型之CPU缓存策略
106 0

热门文章

最新文章