原创

如何在javascript中检测一个对象是否已经被垃圾回收了

如何在javascript中检测一个对象是否已经被垃圾回收了

随着前端应用越来越复杂,内存泄露也同样影响了javascript应用。关键对象是否被正常回收,应该怎么跟踪。

原文 http://stevehanov.ca/blog/?id=148

如果你在使用javascript写一个应用程序,迟早你会担心内存泄露。不过知道是否存在内存泄露都挺困难的,下面就是个有用技巧。

WeakMap

一开始,你可能会想使用WeakMap。WeakMap/WeakSet 能够持有对象,不过不会阻止对象被垃圾回收。一个对象的实例被垃圾回收了,它就会从 WeakMap/WeakSet中移出。

所以,一个明显的解决方案是检查一个对象是否还在WeakMap中。如果找不到,那么它就被回收了。

可惜这个方法行不通。

问题在于WeakMap和WeakSet的设计使你知道对象在那里,才能查找对象。这是因为,为了查找一个对象,你需要已经持有这些对象的引用。这些集合甚至没有一个length 的方法。

为了检查一个对象是否在WeakMap中,你必须已经持有一个对它的引用,所以你也阻止了这个对象被垃圾回收。

所以,它们有什么用?WeakMap 最好是用来把对象联系到一起。比如,如果你有一堆<img> 元素,而且你想把一些数据和他们联系起来,你可能简单的使用 img.myextraproperty="blah".不过你的IDE可能会警告你,因为HTMLImageElement 没有这个属性。换个方法,你可以用WeakMap。如果额外的属性是一个值 true ,那么可以用WeakSet。

实际的解决方案

有些浏览器,包括Chrome 不过不包含Firefox,有能力检测javascript使用的内存大小。所以检测一个对象是否存在的方法,是让它足够大到可以显著的影响内存占用的大小。

在下面的代码中,我用WeakMap把你传入的任意对象关联了一个1G大的对象。当这个对象没有引用了,垃圾回收开始执行,你可以预期至少1GB的内存会被回收。这就是这个代码要的检查的东西。这个过程至少会用掉10秒钟,因为看起来Chrome每10秒运行一次垃圾回收。

/** 检测一个对象是否被回收@param obj 检测对象@param freeFn 释放对象的方法@returns 返回一个promise  {freed: boolean, memoryDiff:number}
@author Steve Hanov <[email protected]>*/function isObjectFreed(obj, freeFn) { return new Promise( (resolve) => { if (!performance.memory) { throw new Error("Browser not supported."); }
// When obj is GC'd, the large array will also be GCd and the impact will // be noticeable. const allocSize = 1024*1024*1024; const wm = new WeakMap([[obj, new Uint8Array(allocSize)]]);
// wait for memory counter to update setTimeout( () => { const before = performance.memory.usedJSHeapSize;
// Free the memory freeFn();
// wait for GC to run, at least 10 seconds setTimeout( () => { const diff = before - performance.memory.usedJSHeapSize; resolve({ freed: diff >= allocSize, memoryDiff: diff - allocSize }); }, 10000); }, 100); });}
let foo = {bar:1};
isObjectFreed(foo, () => foo = null).then( (result) => { document.write(`Object GCd:${result.freed}, ${result.memoryDiff} bytes freed`)}, (error) => { document.write(`Error: ${error.message}`)})


如何使用

这个方法是我的javascript绘画应用  Zwibbler 项目的测试程序的一部分。它有个destory()方法来移除所有资源。不过有时,我会忘记移除一些事件监听器,这会保持对整个应用的引用。所以当在一些 React或者Angular上使用的时候,它会被框架反复的显示/隐藏,这时候资源完全释放就至关重要了。


正文到此结束
该篇文章的评论功能已被站长关闭