Blob对象与createObjectURL的GC问题及自动撤销URL实现方案
问题解答
1. Blob的垃圾回收规则
当调用URL.createObjectURL(blob)时,浏览器会在内部保留对该Blob的引用——哪怕你的代码里已经没有Blob的变量引用,这个Blob也不会被垃圾回收,直到你主动调用URL.revokeObjectURL(url)撤销该URL,或者页面卸载时才会释放。
看你给出的第一个代码示例:
async function getImageBlobURL() { const res = await fetch('/image.png'); const blob = await res.blob(); // will it be GCed? const url = URL.createObjectURL(blob); return url; } const img = document.createElement('img'); const url = await getImageBlobURL(); img.url = url; document.body.append(img);
函数执行完毕后,blob变量的作用域结束,但由于url对应的内部引用仍存在,这个Blob不会被GC,会持续占用内存,直到页面关闭或你主动调用撤销方法。
2. 自动撤销URL的实现说明
你用FinalizationRegistry实现自动撤销的思路是可行的,但有几个关键注意点:
FinalizationRegistry的回调执行时机不确定,浏览器不会保证Blob被标记为垃圾后立刻触发回调,可能存在延迟,极端场景下甚至不会执行。- 必须确保使用URL的代码同时持有Blob的引用(如示例中的
img._blobObj = blob),否则若Blob先被GC但图片仍在使用URL,会导致图片加载失败。
你的实现代码可以正常工作,但要明确其局限性:
const finalizationRegistry = new FinalizationRegistry<string>((url) => { URL.revokeObjectURL(url); }); /** * 获取Blob的URL,当Blob被垃圾回收时会自动撤销该URL。 * 使用此URL的代码必须同时持有该Blob的引用。 */ export function getAutoRevokableBlobUrl(blob: Blob) { const url = URL.createObjectURL(blob); finalizationRegistry.register(blob, url); return url; } // 使用示例: const img = document.createElement('img'); const url = getAutoRevokableBlobUrl(blob); img.url = url; // 当img被移除并垃圾回收后,blob也会被回收,URL会在FinalizationRegistry中自动撤销 img._blobObj = blob; document.body.append(img);
如果是对内存占用敏感的场景(比如处理大量Blob资源),建议还是主动调用URL.revokeObjectURL及时释放内存,不要完全依赖FinalizationRegistry的自动回调。
内容的提问来源于stack exchange,提问作者DisLido
相关产品推荐
相关产品推荐

