优先用WeakReference<T>,因类型安全、无装箱、编译期校验;WeakReference需手动转换且易NullReferenceException;缓存须配合GetOrCreate模式并注意线程安全。

WeakReference 不是用来“防止内存泄漏”的工具,它只解决一个具体问题:你得持有对象引用,但又不能让它因此赖在内存里不走。用错地方,反而会炸出 NullReferenceException 或逻辑跳变。
WeakReference 和 WeakReference 该怎么选
优先用 WeakReference<t></t>,.NET 4.5+ 全支持,类型安全、无装箱、不用手动 cast。
-
WeakReference的Target是object,每次取值都要as T或强制转换,一旦Target == null还没判空就调方法,立刻崩 -
WeakReference<t></t>提供TryGetTarget(out T target),返回bool+ 输出参数,天然规避 NRE,也省去空检查的冗余分支 - 泛型版构造时就锁死类型,编译期就能拦住类型误用;非泛型版 runtime 才暴露问题
缓存场景下 WeakReference 的典型错误写法
弱引用缓存不是“自动续命”,它是“随时可能断联”。常见翻车点集中在访问模式上:
- 直接链式调用:
weakRef.Target.SomeMethod()——Target可能在取值瞬间被 GC 回收,多线程下尤其危险 - 缓存
Target到局部变量后反复用:var obj = weakRef.Target; obj.DoA(); obj.DoB();—— 第二个调用时obj可能已为 null - 在
Finalize或终结器里访问Target—— 此时对象已进入回收流程,行为未定义,TryGetTarget必然返回false - 把
IsAlive当成“安全开关”:if (weakRef.IsAlive) { weakRef.Target.DoWork(); }——IsAlive和Target读取不是原子操作,中间照样可能被回收
WeakReference 缓存必须配合 GetOrCreate 模式
单靠 WeakReference 做缓存毫无意义,它不负责重建。真正可用的缓存逻辑必须是“获取或创建”闭环:
- 先查字典(如
Dictionary<string, WeakReference<T>>),再TryGetTarget;失败就调Func<T>创建新实例 - 键建议用业务标识(如文件路径、类型
typeof(T)),避免用随机对象当 key 导致哈希冲突或泄漏 - 这个模式本身线程不安全,高并发下需加锁或换
ConcurrentDictionary,但注意锁粒度——别整个字典一把锁,否则性能反降 - 不要对小对象(WeakReference 自身有开销,可能比缓存对象还重
WeakReference 无法替代 IDisposable 或事件解绑
它管不了非托管资源,也破不开所有循环引用。
- 文件句柄、数据库连接、
Timer、GDI 对象等,仍必须显式调用Dispose()或Close(),WeakReference对它们完全无效 - 事件订阅引发的泄漏,本质是委托强持有了订阅者,仅靠
WeakReference绑定发布者没用;得用弱事件模式(如WeakEventManager)或手动-=解绑 - 父子双向引用中,只改一方为弱引用才有效;若双方都弱,关系就断了,业务逻辑可能失效
最常被忽略的一点:WeakReference 的生命周期和 GC 行为强耦合,而 GC 触发时机不可控。你在开发机测得“稳稳不回收”,上线后因内存压力大、GC 频繁,缓存命中率可能断崖下跌——别把它当稳定缓存用,只当“可有可无的加速垫”。


















