weakref本身不阻碍gc,使对象在无强引用时可被正常回收,避免内存泄漏;finalizationregistry则在对象被回收后触发清理回调,实现自动释放资源。

弱引用本身不直接“配合”垃圾回收,而是让对象在无强引用时能被 GC 正常回收,从而避免因缓存、映射等结构长期持有所致的内存泄漏。关键在于:弱引用不阻碍回收,而 FinalizationRegistry 可感知回收时机,实现自动清理。
用 WeakRef 持有大对象,不阻断 GC
WeakRef 是对对象的“可丢弃”引用。只要对象没有其他强引用,即使 WeakRef 还存在,GC 仍可回收它。
- 适合缓存 DOM 节点、大型 JSON 解析结果、Canvas 图像数据等生命周期不确定的大对象
- 不能通过 WeakRef.get() 总是拿到值——返回可能为 undefined,需做存在性判断
- 避免把 WeakRef 存在全局 Map 里却不清理键,否则键会累积,造成内存浪费(键本身小,但逻辑上“残留”)
用 FinalizationRegistry 注册回收回调,自动清理关联数据
WeakRef 单独使用只能“被动等待”,加上 FinalizationRegistry 才能主动响应回收事件。
- 注册时传入一个 cleanup 回调,当 WeakRef 指向的对象被 GC 回收后,该回调会被异步触发
- 回调中可安全删除缓存 Map 中对应的 key,或释放关联资源(如关闭 WebSocket、释放 ArrayBuffer)
- 注意:回调执行时机不确定,不可依赖其顺序或立即性;也不能在回调里再强引用该对象(已销毁)
典型缓存模式:WeakCache 类结构
把 WeakRef 和 FinalizationRegistry 组合封装,形成带自动清理能力的缓存容器。
- 内部用 Map 存键 → WeakRef 值,同时用 FinalizationRegistry 监听每个 WeakRef 的目标对象
- 注册时将 key 作为附加参数传入 registry,回调中就能精准删除对应键
- get 时先 weakRef.get(),若为 undefined 则说明已被回收,直接返回 null 或重新计算
- 比普通 Map 缓存更安全,尤其适用于组件级缓存(如 React 中按 element 缓存布局信息)
哪些场景特别适合,哪些要避开
弱引用不是万能解药,用错反而增加复杂度。
- 适合:DOM 节点缓存、图表实例映射、用户上传的临时文件对象、API 响应快照
- 不适合:需要稳定存在、必须保证 get() 总能返回值的场景(比如配置中心、单例服务)
- 警惕:在 WeakRef.get() 后立刻赋给新变量——这会创建新的强引用,抵消弱引用意义
- 替代方案:对确定生命周期的对象,优先用显式销毁(如 dispose() 方法 + 清理定时器/事件监听)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











