概述
基本分析
ActiveResource是Glide中的一个内存缓存类,这个内存缓存类和LruResourceCache内存缓存类是不一样的。
缓存到ActiveResources中的EngineResource是正在被使用的,也就是有其它对象持有(引用)它。EngineResource中有个acquire变量表示有几个地方持有了这个Resource,如果acquire等于0了,这时才会把EngineResorce从ActiveResource中移除放到LruResourceCache中(也可能是直接释放,具体看设置)。acquire等于0已经后续的操作都是在主线程中执行的,这个是正常的流程,具体分析可以看Glide之EngineJob和EngineResource。
可是还有一种情况,称之为特殊情况,就是在gc的时候,EngineResource的拥有者被释放了,我们假设EngineResource的所有拥有者都被释放了。按照ActiveResources的作用地位,EngineResource是需要从ActiveResources中移除的。如果ActiveResource中缓存数据是采用强引用方式存储EngineResource的话就会导致无法释放,而且ActiveResource也并不知道EngineResource可以被释放了。
那怎么才能让ActiveResources中的EngineResource自动释放同时可以感知这个释放进而可以做一些事情呢?那就是WeakReference和ReferenceQueue。
为了避免这个问题,ActiveResources采用了弱引用的方式,这样就可以避免上面的问题了。我们具体分析一下,因为采用了WeakReference,EngineResource所有的强引用都断开了,EngineResource就会被释放,包装EngineResource的Reference就会被放到ReferenceQueue里面。这样虽然解决了内存泄漏的问题,可是EngineResource从ActiveResources释放后没有放到LruCache中,这样也不对呀。
为了解决这个问题,ActiveResource启动了一个线程专门去扫描ReferenceQueue,当然这个线程的优先级很低是THREAD_PRIORITY_BACKGROUND。只要调用ReferenceQueue的remove方法能取到元素,就说明EngineResource被回收了,可以移动到LruCache里面了。不过在使用过程中有个技巧,Reference包装了EngineResource,在回收的时候,被包装的EngineResource是会被回收的,可是我们需要把它保存到LruCache中,所以,在WeakReference的继承类中有个变量又引用了EngineResource。
EngineResourc从ActiveResources移动到LruCache中是在Engine的回调中进行的。
ActiveResources中是用HashMap,value是WeakReference的方式缓存的,因为它是短期缓存的,所以没有限制大小。
同步相关
在ActiveResources代码中我们能看到synchronized使用,我们只分析一处,这处和EngineResource需要一块分析,我们看下面代码
void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
// Fixes a deadlock where we normally acquire the Engine lock and then the ActiveResources lock
// but reverse that order in this one particular test. This is definitely a bit of a hack...
synchronized (listener) {
synchronized (this) {
activeEngineResources.remove(ref.key);
if (!ref.isCacheable || ref.resource == null) {
return;
}
EngineResource<?> newResource =
new EngineResource<>(ref.resource, /*isCacheable=*/ true, /*isRecyclable=*/ false);
newResource.setResourceListener(ref.key, listener);
listener.onResourceReleased(ref.key, newResource);
}
}
}
void release() {
// To avoid deadlock, always acquire the listener lock before our lock so that the locking
// scheme is consistent (Engine -> EngineResource). Violating this order leads to deadlock
// (b/123646037).
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);
}
}
}
}
cleanupActiveReference是ActiveResources中的方法,release是EngineResource中的方法。它们中都需要用到listener,这个listener就是Engine。我们看上面的基本分析,知道这个两个方法是执行在不同线程里面的,所以把listener当成了锁。
大家对照上面的解释,看源码就很好理解了
package com.bumptech.glide.load.engine;
import android.os.Process;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import com.bumptech.glide.load.Key;
import com.bumptech.glide.load.engine.EngineResource.ResourceListener;
import com.bumptech.glide.util.Executors;
import com.bumptech.glide.util.Preconditions;
import com.bumptech.glide.util.Synthetic;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
final class ActiveResources {
private final boolean isActiveResourceRetentionAllowed;
private final Executor monitorClearedResourcesExecutor;
@VisibleForTesting
final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
private final ReferenceQueue<EngineResource<?>> resourceReferenceQueue = new ReferenceQueue<>();
private ResourceListener listener;
private volatile boolean isShutdown;
@Nullable
private volatile DequeuedResourceCallback cb;
ActiveResources(boolean isActiveResourceRetentionAllowed) {
this(
isActiveResourceRetentionAllowed,
java.util.concurrent.Executors.newSingleThreadExecutor(
new ThreadFactory() {
@Override
public Thread newThread(@NonNull final Runnable r) {
return new Thread(
new Runnable() {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
r.run();
}
},
"glide-active-resources");
}
}));
}
@VisibleForTesting
ActiveResources(
boolean isActiveResourceRetentionAllowed, Executor monitorClearedResourcesExecutor) {
this.isActiveResourceRetentionAllowed = isActiveResourceRetentionAllowed;
this.monitorClearedResourcesExecutor = monitorClearedResourcesExecutor;
monitorClearedResourcesExecutor.execute(
new Runnable() {
@Override
public void run() {
cleanReferenceQueue();
}
});
}
void setListener(ResourceListener listener) {
synchronized (listener) {
synchronized (this) {
this.listener = listener;
}
}
}
synchronized void activate(Key key, EngineResource<?> resource) {
ResourceWeakReference toPut =
new ResourceWeakReference(
key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
removed.reset();
}
}
synchronized void deactivate(Key key) {
ResourceWeakReference removed = activeEngineResources.remove(key);
if (removed != null) {
removed.reset();
}
}
@Nullable
synchronized EngineResource<?> get(Key key) {
ResourceWeakReference activeRef = activeEngineResources.get(key);
if (activeRef == null) {
return null;
}
EngineResource<?> active = activeRef.get();
if (active == null) {
cleanupActiveReference(activeRef);
}
return active;
}
@SuppressWarnings({"WeakerAccess", "SynchronizeOnNonFinalField"})
@Synthetic
void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
// Fixes a deadlock where we normally acquire the Engine lock and then the ActiveResources lock
// but reverse that order in this one particular test. This is definitely a bit of a hack...
synchronized (listener) {
synchronized (this) {
activeEngineResources.remove(ref.key);
if (!ref.isCacheable || ref.resource == null) {
return;
}
EngineResource<?> newResource =
new EngineResource<>(ref.resource, /*isCacheable=*/ true, /*isRecyclable=*/ false);
newResource.setResourceListener(ref.key, listener);
listener.onResourceReleased(ref.key, newResource);
}
}
}
@SuppressWarnings("WeakerAccess")
@Synthetic void cleanReferenceQueue() {
while (!isShutdown) {
try {
ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove();
cleanupActiveReference(ref);
// This section for testing only.
DequeuedResourceCallback current = cb;
if (current != null) {
current.onResourceDequeued();
}
// End for testing only.
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
@VisibleForTesting
void setDequeuedResourceCallback(DequeuedResourceCallback cb) {
this.cb = cb;
}
@VisibleForTesting
interface DequeuedResourceCallback {
void onResourceDequeued();
}
@VisibleForTesting
void shutdown() {
isShutdown = true;
if (monitorClearedResourcesExecutor instanceof ExecutorService) {
ExecutorService service = (ExecutorService) monitorClearedResourcesExecutor;
Executors.shutdownAndAwaitTermination(service);
}
}
@VisibleForTesting
static final class ResourceWeakReference extends WeakReference<EngineResource<?>> {
@SuppressWarnings("WeakerAccess") @Synthetic final Key key;
@SuppressWarnings("WeakerAccess") @Synthetic final boolean isCacheable;
@Nullable @SuppressWarnings("WeakerAccess") @Synthetic Resource<?> resource;
@Synthetic
@SuppressWarnings("WeakerAccess")
ResourceWeakReference(
@NonNull Key key,
@NonNull EngineResource<?> referent,
@NonNull ReferenceQueue<? super EngineResource<?>> queue,
boolean isActiveResourceRetentionAllowed) {
super(referent, queue);
this.key = Preconditions.checkNotNull(key);
this.resource =
referent.isCacheable() && isActiveResourceRetentionAllowed
? Preconditions.checkNotNull(referent.getResource()) : null;
isCacheable = referent.isCacheable();
}
void reset() {
resource = null;
clear();
}
}
}
最后
以上就是风中芝麻为你收集整理的Glide之ActiveResource(一级内存缓存)的全部内容,希望文章能够帮你解决Glide之ActiveResource(一级内存缓存)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复