weakref 无法管理 webgl 纹理,因其是宿主对象;必须显式调用 gl.deletetexture() 释放 gpu 内存,weakref 和 finalizationregistry 仅适用于 js 层元数据,不可用于纹理句柄本身。

WeakRef 本身不能让 WebGL 纹理“用完即丢”,它只负责不阻止回收;真正释放 GPU 内存必须调用 gl.deleteTexture(),且这一步必须由你主动触发。
WeakRef 对 WebGL 纹理对象无效
WebGL 纹理(WebGLTexture)对象本身不支持弱引用——new WeakRef(texture) 会抛出 TypeError: Cannot create a weak reference to a non-object 或静默失败(取决于浏览器实现),因为 WebGLTexture 是宿主对象(host object),不在 JavaScript 引擎的 GC 管理范围内。WeakRef 只能包裹 JS 层可追踪对象(如普通对象、数组、类实例),不能包裹底层 GPU 资源句柄。
- 你看到的
texture变量只是指向 GPU 中某块内存的轻量句柄,不是资源本体 - WeakRef 无法穿透到 GPU 层,也无法监听
gl.deleteTexture()是否被调用 - 试图对
texture做weakRef.deref()没有意义:它永远返回原句柄,哪怕 GPU 内存早已被删
缓存键应使用可弱引用的标识,而非纹理本身
想实现“按需加载 + 自动清理”的缓存逻辑,关键不是弱引用纹理,而是弱引用**能代表纹理的 JS 对象**,比如一个封装了 ID、尺寸、URL 的轻量元数据对象。
- 缓存结构推荐:
Map<string id: string width: number height: url:></string>,而不是Map<string webgltexture></string> - 若需关联纹理生命周期,可用
WeakMap<htmlimageelement webgltexture></htmlimageelement>—— 因为HTMLImageElement支持弱引用,且图像加载完成是纹理创建的前提 - 避免用
WeakRef包裹WebGLRenderingContext:上下文本身不可弱引用,且一旦丢失就无法再操作任何纹理
FinalizationRegistry 无法帮你删掉 GPU 纹理
FinalizationRegistry 注册的是 JS 对象,不是 GPU 句柄。即使你注册了某个包装对象(如 { texture, id }),它的回调触发只说明该 JS 对象被 GC,不代表 texture 还活着,更不代表 GPU 内存未泄漏。
- 注册时传入的
holdings必须是简单值(如id字符串),不能是含texture的对象(否则形成强引用,阻止 GC) - 回调中你只能拿到
id,无法访问原texture,因此无法调用gl.deleteTexture() - 真正安全的释放时机是明确知道“这个纹理不再会被任何后续帧使用”,比如切换场景、卸载组件、或纹理被更高清版本替换时
可行的“用完即丢”模式:显式销毁 + 弱引用元数据兜底
把缓存设计成两级:JS 层元数据用 WeakMap 或 WeakRef 管理,GPU 资源靠业务逻辑显式销毁。FinalizationRegistry 仅用于日志报警,不用于执行释放。
- 加载纹理时:
const meta = { id, url, timestamp: Date.now() }; cache.set(id, meta); registry.register(meta, id); - 使用前检查:
if (!cache.has(id)) loadAndCreateTexture(id); - 确定不用时:
gl.deleteTexture(texture); texture = null;(解除 JS 引用) - FinalizationRegistry 回调里只做:
console.warn(`Texture metadata for ${id} leaked — no explicit dispose()`); - 关键约束:所有
gl.createTexture()必须有对应gl.deleteTexture(),且不能依赖 GC 触发
最易被忽略的一点:WebGL 上下文丢失(webglcontextlost)后,所有纹理句柄自动失效,但 JS 变量仍非 null。此时调用 gl.deleteTexture() 无效果,必须先检查 gl.isTexture(texture) 再操作。










