WeakReference 的核心用途是:在不阻止 GC 回收的前提下,临时“惦记着”一个对象——它不是为了长期持有,而是为了“有就用,没了再建”。
WeakReference
最典型的使用场景
它不是万能胶,而是专治几类特定问题的“止痛贴”:
大对象缓存:比如一个 50MB 的图像预处理结果、一棵深层嵌套的树视图数据。用户切走页面后,你不想强留它占内存,但返回时又希望“如果还在,就直接用”,WeakReference正好卡在这个平衡点。 事件监听器防泄漏:UI 控件订阅了 ViewModel 的事件,但 ViewModel 生命周期更长。若不手动
Unsubscribe,控件就永远被强引用链拴住——改用弱引用管理订阅者(需配合自定义事件代理),可让控件随 UI 一起被回收。 避免循环引用导致的内存滞留:父对象持子对象强引用没问题,但子对象反过来强引用父对象,就容易形成 GC 不动的“孤岛”。把子→父的引用换成
WeakReference,就能打破僵局。
WeakReference
的两个构造函数参数差异很关键
别只用无参构造!
trackResurrection这个布尔值决定的是“对象被终结器复活后,弱引用还作不作数”:
new WeakReference(obj)→ 短弱引用(
GCHandleType.Weak):终结器执行前就清空
Target。对象一旦进 finalization 队列,你就再也拿不到它了。
new WeakReference(obj, true)→ 长弱引用(
GCHandleType.WeakTrackResurrection):即使对象被
GC.ReRegisterForFinalize(this)复活,
Target仍可能非 null(但极罕见,且不可靠)。
绝大多数业务场景选默认(
false)就够了;除非你在写底层框架、对象池,且明确需要感知复活状态,否则加
true只会增加不确定性。
访问 Target
前必须检查,否则必崩
这是新手踩坑最密集的地方:弱引用不是“延迟加载”,而是“随时可能消失”。下面这段代码看着合理,实则危险:
var weak = new WeakReference(myBigObject); // ... 一段时间后 var obj = weak.Target as MyType; // ❌ Target 可能已是 null! obj.DoSomething(); // NullReferenceException!
正确姿势永远是:
if (weak.TryGetTarget(out MyType obj))
{
obj.DoSomething(); // ✅ 安全
}
else
{
// 重建或跳过
}
注意:
IsAlive属性已过时(.NET 5+ 标记为 obsolete),它不能保证
Target非 null —— 因为 GC 可能在
IsAlive == true后瞬间回收对象,紧接着
Target就变 null。所以务必用线程安全的
TryGetTarget。
真正难的不是写对那几行代码,而是想清楚:这个对象“值得弱引用吗?”——太小(如
int包装、短字符串)不值得;重建成本太高(如需远程调用 DB 初始化)也不适合;只有“大 + 易重建 + 访问频次低”的对象,才配得上
WeakReference这张牌。
