网站首页 > 技术文章 正文
原文链接: http://stevehanov.ca/blog/?id=148
原作者:steve hanov
如果你在使用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 <steve.hanov@gmail.com>
*/
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上使用的时候,它会被框架反复的显示/隐藏,这时候资源完全释放就至关重要了。
如何使用
猜你喜欢
- 2024-10-24 Service Workers - JS API 简介(servicedescriptor)
- 2024-10-24 web性能优化的15条实用技巧(web应用性能优化思路)
- 2024-10-24 如何在 Service Worker 重新启动时重用信息
- 2024-10-24 Python在selenium里面注入JavaScript程序的方法
- 2024-10-24 requireJS 实战(requirejs define)
- 2024-10-24 面试妥了!2020 爬虫面试题目合集(爬虫面试经历)
- 2024-10-24 Nest.js 从零到壹系列(一):项目创建&路由设置&模块
- 2024-10-24 JS小知识,分享 7 个高频的工具函数,也许你用的上
- 2024-10-24 如何使用Playwright优化测试性能(play—player)
- 2024-10-24 JavaScript开发人员都应知道的异步迭代,你会了吗?
- 11-26Win7\8\10下一条cmd命令可查得笔记本电脑连接过的Wifi密码
- 11-26一文搞懂MySQL行锁、表锁、间隙锁详解
- 11-26电脑的wifi密码忘记了?一招教你如何找回密码,简单明了,快收藏
- 11-26代码解决忘记密码问题 教你用CMD命令查看所有连接过的WIFI密码
- 11-26CMD命令提示符能干嘛?这些功能你都知道吗?
- 11-26性能测试之慢sql分析
- 11-26论渗透信息收集的重要性
- 11-26如何查看电脑连接过的所有WiFi密码
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)