SharedReference 是 CloseableReference 的内部组件,负责管理资源的引用计数和释放逻辑,是 Fresco 实现内存安全和线程安全的关键。
- SharedReference 概述
SharedReference 是 Fresco 中用于管理共享资源的引用计数类,位于 com.facebook.common.references 包。它是 CloseableReference 的核心组成部分,负责跟踪资源的引用计数,并在计数降为 0 时触发资源释放。SharedReference 的设计灵感来源于 C++ 的 std::shared_ptr,通过引用计数实现资源的共享和自动清理,特别适合管理内存敏感资源(如 Bitmap 和 PooledByteBuffer)。
核心功能:
- 引用计数:跟踪资源被多少个 CloseableReference 引用。
- 线程安全:使用同步机制确保多线程环境下引用计数的正确性。
- 资源释放:当引用计数为 0 时,调用 ResourceReleaser 释放资源。
- 内存安全:防止资源过早释放或泄漏。
源码位置:
-
SharedReference 是 CloseableReference 的静态内部类,定义在 CloseableReference.java 中。
-
相关类:
- CloseableReference:包装 SharedReference,提供用户接口。
- ResourceReleaser:定义资源释放逻辑。
- SharedReference 设计原理
SharedReference 的设计基于 引用计数(Reference Counting)模型,核心思想是:
- 一个资源(如 Bitmap 或 PooledByteBuffer)由多个 CloseableReference 共享。
- SharedReference 持有资源的实际引用和引用计数,管理资源的生命周期。
- 当所有 CloseableReference 释放引用(引用计数降为 0)时,SharedReference 触发资源释放。
关键组件:
- mValue:存储资源对象(如 CloseableImage 或 PooledByteBuffer)。
- mRefCount:引用计数,记录当前有多少 CloseableReference _引用该资源。
- mResourceReleaser:ResourceReleaser 实例,定义资源的释放逻辑。
- 同步机制:使用 synchronized 确保引用计数和资源访问的线程安全。
工作流程:
- 创建:SharedReference 初始化时,持有资源并设置引用计数为 1。
- 增加引用:CloseableReference 克隆时,调用 addReference,引用计数 +1。
- 减少引用:CloseableReference 关闭时,调用 deleteReference,引用计数 -1。
- 释放资源:引用计数降为 0 时,调用 mResourceReleaser.release 释放资源。
- SharedReference 源码分析
以下通过源码详细解析 SharedReference 的实现。
源码定义
SharedReference 是 CloseableReference 的静态内部类:
java
public class CloseableReference<T> {
static class SharedReference<T> {
private T mValue;
private int mRefCount;
private final ResourceReleaser<T> mResourceReleaser;
public SharedReference(T value, ResourceReleaser<T> resourceReleaser) {
mValue = Preconditions.checkNotNull(value);
mResourceReleaser = Preconditions.checkNotNull(resourceReleaser);
mRefCount = 1;
}
public synchronized T get() {
return mValue;
}
public synchronized int getRefCount() {
return mRefCount;
}
public synchronized void addReference() {
if (mRefCount <= 0) {
throw new IllegalStateException("Cannot add reference to finalized SharedReference");
}
mRefCount++;
}
public synchronized void deleteReference() {
if (mRefCount <= 0) {
throw new IllegalStateException("Cannot delete reference from finalized SharedReference");
}
if (--mRefCount == 0) {
Preconditions.checkNotNull(mValue);
T temp = mValue;
mValue = null;
mResourceReleaser.release(temp);
}
}
public synchronized boolean isValid() {
return mRefCount > 0;
}
}
}
关键方法解析:
-
构造方法:
- 初始化资源(mValue)、释放器(mResourceReleaser)和引用计数(mRefCount = 1)。
- 使用 Preconditions.checkNotNull 确保参数非空。
-
get:
- 返回资源对象,同步访问确保线程安全。
-
addReference:
- 增加引用计数,检查 mRefCount > 0 防止操作已释放的资源。
-
deleteReference:
- 减少引用计数,若降为 0,释放资源并清空 mValue。
- 使用临时变量 temp 确保释放前资源不被修改。
-
isValid:
- 检查资源是否有效(mRefCount > 0)。
与 CloseableReference 的交互
CloseableReference 是 SharedReference 的外层包装,提供用户友好的接口:
-
创建:
java
public static <T> CloseableReference<T> of(T t, ResourceReleaser<T> resourceReleaser) { if (t == null) { return null; } return new CloseableReference<>(new SharedReference<>(t, resourceReleaser)); }- 创建 SharedReference,并用 CloseableReference 包装。
-
克隆:
java
public synchronized CloseableReference<T> cloneOrNull() { if (isValid()) { return new CloseableReference<>(mSharedReference); } return null; }- 新建 CloseableReference,调用 mSharedReference.addReference()。
-
关闭:
java
public synchronized void close() { if (!mIsClosed) { mIsClosed = true; mSharedReference.deleteReference(); } }- 调用 mSharedReference.deleteReference() 减少引用计数。
ResourceReleaser 示例
ResourceReleaser 定义资源的释放逻辑,例如:
-
释放 Bitmap:
java
ResourceReleaser<Bitmap> bitmapReleaser = bitmap -> { if (bitmap != null && !bitmap.isRecycled()) { bitmap.recycle(); } }; -
释放 PooledByteBuffer:
java
ResourceReleaser<PooledByteBuffer> bufferReleaser = buffer -> { if (buffer != null) { buffer.close(); // 归还到内存池 } };
- SharedReference 在 Fresco 中的工作流程
SharedReference 是 CloseableReference 的核心,贯穿 Fresco 的缓存、解码和渲染流程。以下是一个典型的工作流程:
-
资源创建:
-
ImagePipeline 解码图片,生成 CloseableImage:
java
CloseableImage image = mImageDecoder.decode(encodedImage, decodeOptions); CloseableReference<CloseableImage> imageRef = CloseableReference.of(image, imageReleaser); -
创建 SharedReference,初始化 mRefCount = 1。
-
-
资源传递:
-
ImagePipeline 返回 imageRef 给 DraweeController,可能多次克隆:
java
CloseableReference<CloseableImage> clonedRef = imageRef.cloneOrNull(); -
每次克隆,SharedReference.addReference() 增加 mRefCount。
-
-
资源使用:
-
DraweeController 使用 imageRef.get() 获取图片,渲染到 DraweeView:
java
mSettableDraweeHierarchy.setImage(imageRef.get(), progress, true); -
SharedReference.get() 提供线程安全的资源访问。
-
-
资源释放:
-
使用完成后,调用 CloseableReference.close():
java
CloseableReference.closeSafely(imageRef); -
SharedReference.deleteReference() 减少 mRefCount,若降为 0,调用 mResourceReleaser.release。
-
-
异常处理:
-
如果调用方忘记 close,Fresco 使用 CloseableReference.closeSafely:
java
public static void closeSafely(CloseableReference<?> ref) { if (ref != null) { ref.close(); } }
-
示例场景(缓存管理):
-
CountingMemoryCache 存储 CloseableReference:
java
CloseableReference<CloseableImage> cache(K key, CloseableReference<CloseableImage> value) { mCachedEntries.put(key, value); return CloseableReference.cloneOrNull(value); } -
查询时返回克隆的引用,增加 mRefCount:
java
CloseableReference<CloseableImage> get(K key) { ValueDescriptor<CloseableImage> value = mCachedEntries.get(key); return value != null ? CloseableReference.cloneOrNull(value.get()) : null; } -
缓存淘汰时释放引用:
java
CloseableReference.closeSafely(evictedValue);
- SharedReference 的内存管理机制
SharedReference 通过以下机制实现高效的内存管理:
-
引用计数:
- mRefCount 精确跟踪资源的使用情况,仅当 mRefCount == 0 时释放资源。
- 防止资源过早释放或泄漏。
-
线程安全:
-
所有方法(get、addReference、deleteReference)使用 synchronized 保护,确保多线程环境下 mRefCount 和 mValue 的一致性。
-
示例(多线程克隆):
java
CloseableReference<CloseableImage> ref = getImageRef(); executor.execute(() -> { CloseableReference<CloseableImage> cloned = ref.cloneOrNull(); try { // 使用 cloned } finally { CloseableReference.closeSafely(cloned); } });
-
-
资源释放:
- deleteReference 在 mRefCount == 0 时调用 mResourceReleaser.release,支持不同类型资源的释放逻辑。
- 释放后,mValue = null 防止后续访问。
-
内存池集成:
-
对于 PooledByteBuffer,SharedReference 与内存池配合,释放时归还缓冲区:
java
ResourceReleaser<PooledByteBuffer> bufferReleaser = buffer -> buffer.close();
-
-
异常鲁棒性:
- 检查 mRefCount > 0 防止操作已释放的资源。
- Preconditions.checkNotNull 确保资源和释放器有效。
- SharedReference 的优点与挑战
优点
-
内存安全:
- 引用计数确保资源仅在不再需要时释放,防止泄漏或过早回收。
-
线程安全:
- synchronized 机制支持高并发场景,适合 Fresco 的异步图片加载。
-
灵活性:
- 通过 ResourceReleaser 支持不同资源类型(Bitmap、PooledByteBuffer 等)。
-
与缓存集成:
- 在 CountingMemoryCache 和 EncodedMemoryCache 中高效管理引用。
-
调试支持:
- getRefCount 和 isValid 方法便于调试引用状态。
挑战
-
性能开销:
- synchronized 锁在高并发场景下可能引入轻微性能开销。
- 引用计数操作(addReference、deleteReference)增加计算成本。
-
使用复杂性:
-
依赖 CloseableReference 的显式 close,开发者需正确管理引用。
-
示例(错误用法可能导致泄漏):
java
CloseableReference<CloseableImage> ref = getImageRef(); // 忘记 close,可能导致泄漏
-
-
调试难度:
- 如果引用未正确释放,排查 mRefCount 异常需要分析引用链。
-
内存占用:
- SharedReference 实例本身占用少量内存,大量资源可能累积开销。
- 与其他机制的对比
为了更全面理解 SharedReference,以下将其与 Glide 的内存管理机制对比:
Glide 的 WeakReference
-
机制:
- 使用 WeakReference 管理活跃资源(ActiveResources),依赖 JVM GC 回收。
- LruResourceCache 存储非活跃资源,无显式引用计数。
-
源码示例:
java
class ActiveResources { private final Map<Key, ResourceWeakReference> activeResources = new HashMap<>(); void activate(Key key, Resource<?> resource) { activeResources.put(key, new ResourceWeakReference(resource)); } } -
特点:
- 简单轻量,GC 自动回收。
- 弱引用可能导致资源过早回收,需重新加载。
对比
| 特性 | SharedReference (Fresco) | WeakReference (Glide) |
|---|---|---|
| 管理方式 | 引用计数,显式释放 | 弱引用,依赖 GC |
| 线程安全 | synchronized保证 | 需额外同步机制 |
| 内存安全 | 严格,需手动close | 依赖 GC,可能延迟或过早回收 |
| 性能开销 | 轻微(锁和计数) | 低(但 GC 可能影响性能) |
| 适用场景 | 高并发、复杂资源管理 | 简单加载、内存非敏感场景 |
| 复杂性 | 较高(需显式管理) | 较低(自动回收) |
- SharedReference:提供精确控制,适合大型应用的复杂内存管理,但需要显式释放。
- WeakReference:简单易用,适合中小型应用,但依赖 GC,可能导致性能波动。
- SharedReference 在 Fresco 缓存中的具体应用
SharedReference 在 Fresco 的缓存机制中起到关键作用,以下是具体场景:
-
Bitmap 缓存(CountingMemoryCache):
-
存储 CloseableReference,每个条目共享一个 SharedReference。
-
缓存查询返回克隆的 CloseableReference,增加 mRefCount:
java
CloseableReference<CloseableImage> cached = mBitmapMemoryCache.get(cacheKey); -
缓存淘汰时,调用 close 减少 mRefCount。
-
-
编码缓存(EncodedMemoryCache):
-
存储 CloseableReference,SharedReference 管理字节缓冲区。
-
解码时,克隆引用传递给 ImageDecoder:
java
CloseableReference<PooledByteBuffer> encoded = mEncodedMemoryCache.get(cacheKey); CloseableReference<CloseableImage> decoded = decodeEncodedImage(encoded);
-
-
磁盘缓存(DiskStorageCache):
-
磁盘缓存数据加载到内存后,创建 CloseableReference,由 SharedReference 管理。
-
示例:
java
BinaryResource resource = mMainDiskCache.getResource(cacheKey); PooledByteBuffer buffer = readResourceToBuffer(resource); CloseableReference<PooledByteBuffer> bufferRef = CloseableReference.of(buffer, bufferReleaser);
-
- 优化建议
基于 SharedReference 的实现,以下是优化建议:
-
确保正确关闭:
-
使用 try-finally 确保 CloseableReference 释放:
java
CloseableReference<CloseableImage> ref = getImageRef(); try { // 使用 ref } finally { CloseableReference.closeSafely(ref); }
-
-
监控引用泄漏:
-
启用 CloseableReferenceLeakTracker 检测未释放的引用:
java
ImagePipelineConfig config = ImagePipelineConfig.newBuilder(context) .setCloseableReferenceLeakTracker(new DefaultCloseableReferenceLeakTracker()) .build();
-
-
减少同步开销:
-
在高并发场景下,尽量减少 cloneOrNull 和 close 的调用频率,合并操作:
java
List<CloseableReference<CloseableImage>> refs = fetchImages(); try { // 批量处理 } finally { for (CloseableReference<CloseableImage> ref : refs) { CloseableReference.closeSafely(ref); } }
-
-
优化 ResourceReleaser:
-
为 PooledByteBuffer 配置合适的内存池大小,减少分配开销:
java
ImagePipelineConfig config = ImagePipelineConfig.newBuilder(context) .setPoolFactory(new PoolFactory(PoolConfig.newBuilder() .setPooledByteBufferPoolParams(maxSize, maxBucketSize) .build())) .build();
-
- 总结
SharedReference 是 Fresco 内存管理的核心组件,通过引用计数和线程安全的同步机制,管理共享资源的生命周期。其设计结合了 mRefCount、mValue 和 ResourceReleaser,确保资源在不再需要时安全释放。源码分析表明,SharedReference 在 CloseableReference 的支持下,贯穿 Fresco 的缓存、解码和渲染流程,特别适合高并发和内存敏感场景。与 Glide 的 WeakReference 相比,SharedReference 提供更严格的控制,但需要显式管理引用。