网站首页 > 技术文章 正文
原文链接: 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开发人员都应知道的异步迭代,你会了吗?
- 1507℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 505℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 484℃MySQL service启动脚本浅析(r12笔记第59天)
- 465℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 462℃启用MySQL查询缓存(mysql8.0查询缓存)
- 442℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 422℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 418℃MySQL server PID file could not be found!失败
- 最近发表
-
- netty系列之:搭建HTTP上传文件服务器
- 让deepseek教我将deepseek接入word
- 前端大文件分片上传断点续传(前端大文件分片上传断点续传怎么操作)
- POST 为什么会发送两次请求?(post+为什么会发送两次请求?怎么回答)
- Jmeter之HTTP请求与响应(jmeter运行http请求没反应)
- WAF-Bypass之SQL注入绕过思路总结
- 用户疯狂点击上传按钮,如何确保只有一个上传任务在执行?
- 二 计算机网络 前端学习 物理层 链路层 网络层 传输层 应用层 HTTP
- HTTP请求的完全过程(http请求的基本过程)
- dart系列之:浏览器中的舞者,用dart发送HTTP请求
- 标签列表
-
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- chromepost (65)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- linux删除一个文件夹 (65)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)