网站首页 > 技术文章 正文
大家好,很高兴又见面了,我是"高级前端?进阶?",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!
今天给大家带来的主题是“WeakMap和Map的区别,WeakMap原理,为什么能被 GC”,话不多说,直接开始!
1.垃圾回收机制
我们知道,程序运行中会有一些垃圾数据不再使用,需要及时释放出去,如果没有及时释放,这就是内存泄露。JS 中的垃圾数据都是由垃圾回收(Garbage Collection,缩写为 GC)器自动回收的,不需要手动释放,它是如何做的呢?
很简单,JS 引擎中有一个后台进程称为垃圾回收器,它监视所有对象,观察对象是否可被访问,然后按照固定的时间间隔周期性的删除掉那些不可访问的对象即可。现在各大浏览器通常用采用的垃圾回收有两种方法:
- 引用计数
- 标记清除
2.引用计数
最早最简单的垃圾回收机制,就是给一个占用物理空间的对象附加一个引用计数器,当有其它对象引用这个对象时,这个对象的引用计数加一,反之解除时就减一,当该对象引用计数为 0 时就会被回收。该方式很简单,但会引起内存泄漏:
// 循环引用的问题
function temp(){
var a={};
var b={};
a.o = b;
b.o = a;
}
这种情况下每次调用 temp 函数,a 和 b 的引用计数都是 2 ,会使这部分内存永远不会被释放,即内存泄漏。现在已经很少使用了,只有低版本的 IE 使用这种方式。
3.标记清除
V8 中主垃圾回收器就采用标记清除法进行垃圾回收。主要流程如下:
- 标记:遍历调用栈,看老生代区域堆中的对象是否被引用,被引用的对象标记为活动对象,没有被引用的对象(待清理)标记为垃圾数据。
- 垃圾清理:将所有垃圾数据清理掉
在开发过程中,如果我们想要让垃圾回收器回收某一对象,就将对象的引用直接设置为 null
var a = {}; // {} 可访问,a 是其引用
a = null; // 引用设置为 null
// {} 将会被从内存里清理出去
但如果一个对象被多次引用时,例如作为另一对象的键、值或子元素时,将该对象引用设置为 null ,该对象是不会被回收的,依然存在。
var a = {};
var arr = [a];
a = null;
console.log(arr)
// [{}]
如果作为 Map 的键呢?
var a = {};
var map = new Map();
map.set(a, '三分钟学前端')
a = null;
console.log(map.keys()) // MapIterator {{}}
console.log(map.values()) // MapIterator {"三分钟学前端"}
如果想让 a 置为 null 时,该对象能被回收,该怎么做?
4.WeakMap vs Map
ES6 考虑到了这一点,推出了: WeakMap 。它对于值的引用都是不计入垃圾回收机制的,所以名字里面才会有一个"Weak",表示这是弱引用(对对象的弱引用是指当该对象应该被GC回收时不会阻止GC的回收行为)。
Map 相对于 WeakMap :
- Map 的键可以是任意类型,WeakMap 只接受对象作为键(null除外),不接受其他类型的值作为键
- Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键; WeakMap 的键是弱引用,键所指向的对象可以被垃圾回收,此时键是无效的
- Map 可以被遍历, WeakMap 不能被遍历
下面以 WeakMap 为例,看看它是怎么解决上面问题的:
var a = {};
var map = new WeakMap();
map.set(a, '三分钟学前端')
map.get(a)
a = null;
上例并不能看出什么?通过 process.memoryUsage 测试一下:
//map.js
global.gc();
// 0 每次查询内存都先执行gc()再memoryUsage(),是为了确保垃圾回收,保证获取的内存使用状态准确
function usedSize() {
const used = process.memoryUsage().heapUsed;
return Math.round((used / 1024 / 1024) * 100) / 100 + "M";
}
console.log(usedSize());
// 1 初始状态,执行gc()和memoryUsage()以后,heapUsed 值为 1.64M
var map = new Map();
var b = new Array(5 * 1024 * 1024);
map.set(b, 1);
global.gc();
console.log(usedSize());
// 2 在 Map 中加入元素b,为一个 5*1024*1024 的数组后,heapUsed为41.82M左右
b = null;
global.gc();
console.log(usedSize());
// 3 将b置为空以后,heapUsed 仍为41.82M,说明Map中的那个长度为5*1024*1024的数组依然存在
执行 node --expose-gc map.js 命令:
其中,--expose-gc 参数表示允许手动执行垃圾回收机制
// weakmap.js
function usedSize() {
const used = process.memoryUsage().heapUsed;
return Math.round((used / 1024 / 1024) * 100) / 100 + "M";
}
global.gc();
// 0 每次查询内存都先执行gc()再memoryUsage(),是为了确保垃圾回收,保证获取的内存使用状态准确
console.log(usedSize());
// 1 初始状态,执行gc()和 memoryUsage()以后,heapUsed 值为 1.64M
var map = new WeakMap();
var b = new Array(5 * 1024 * 1024);
map.set(b, 1);
global.gc();
console.log(usedSize());
// 2 在 Map 中加入元素b,为一个 5*1024*1024 的数组后,heapUsed为41.82M左右
b = null;
global.gc();
console.log(usedSize());
// 3 将b置为空以后,heapUsed 变成了1.82M左右,说明WeakMap中的那个长度为5*1024*1024的数组被销毁了
执行 node --expose-gc weakmap.js 命令:
上面代码中,只要外部的引用消失,WeakMap 内部的引用,就会自动被垃圾回收清除。由此可见,有了它的帮助,解决内存泄漏就会简单很多。最后看一下 WeakMap:
5.WeakMap
WeakMap 对象是一组键值对的集合,其中的键是弱引用对象,而值可以是任意。
注意,WeakMap 弱引用的只是键名,而不是键值,键值依然是正常引用。
WeakMap 中,每个键对自己所引用对象的引用都是弱引用,在没有其他引用和该键引用同一对象,这个对象将会被垃圾回收(相应的key则变成无效的),所以,WeakMap 的 key 是不可枚举的。
属性:
- constructor:构造函数
方法:
- has(key):判断是否有 key 关联对象
- get(key):返回key关联对象(没有则则返回 undefined)
- set(key):设置一组key关联对象
- delete(key):移除 key 的关联对象
let myElement = document.getElementById('logo');
let myWeakmap = new WeakMap();
myWeakmap.set(myElement, {timesClicked: 0});
myElement.addEventListener('click', function() {
let logoData = myWeakmap.get(myElement);
logoData.timesClicked++;
}, false);
除了 WeakMap 还有 WeakSet 都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄漏。另外还有 ES12 的 WeakRef ,感兴趣的可以了解下。
参考资料
原文链接:
https://github.com/sisterAn/blog/issues/83
猜你喜欢
- 2024-11-24 赶快收藏!图灵前端图书学习路线图
- 2024-11-24 什么是BitMap?BitMap技术的原理和应用
- 2024-11-24 何时使用 Map 来代替普通的 JS 对象
- 2024-11-24 go语言深入Gin框架内幕(二)
- 2024-11-24 2.7k star!MindMap 助你轻松绘制思维导图,高效工作必备!
- 2024-11-24 Python小知识,如何使用 Map, Filter 和 Reduce 内置函数
- 2024-11-24 解密 JavaScript 中的数据结构:Map vs Object
- 2024-11-24 Vue短文:如何使 Map 和 Set 类型的数据具有响应性?
- 2024-11-24 前端最常用的25个正则表达式,代码效率提高 80%
- 2024-11-24 从0到1实现一个前端监控系统
你 发表评论:
欢迎- 07-10Oracle 与 Google Cloud 携手大幅扩展多云服务
- 07-10分享收藏的 oracle 11.2.0.4各平台的下载地址
- 07-10Oracle 和 Microsoft 推出 Oracle Exadata 数据库服务
- 07-10Oracle Database@Azure 推进到南美等新区域并增加了新服务
- 07-10Oracle宣布推出 Oracle Database@AWS 的有限预览版
- 07-10Oracle与Nextcloud合作,推出主权云上的安全协作平台
- 07-10NodeRED魔改版连接MsSql、PostgreSQL、MySQL、OracleDB存储无忧
- 07-10对于企业数据云备份,“多备份”承诺的是成本更低,管理更高效#36氪开放日深圳站#
- 601℃几个Oracle空值处理函数 oracle处理null值的函数
- 593℃Oracle分析函数之Lag和Lead()使用
- 581℃0497-如何将Kerberos的CDH6.1从Oracle JDK 1.8迁移至OpenJDK 1.8
- 578℃Oracle数据库的单、多行函数 oracle执行多个sql语句
- 573℃Oracle 12c PDB迁移(一) oracle迁移到oceanbase
- 566℃【数据统计分析】详解Oracle分组函数之CUBE
- 552℃最佳实践 | 提效 47 倍,制造业生产 Oracle 迁移替换
- 547℃Oracle有哪些常见的函数? oracle中常用的函数
- 最近发表
-
- Oracle 与 Google Cloud 携手大幅扩展多云服务
- 分享收藏的 oracle 11.2.0.4各平台的下载地址
- Oracle 和 Microsoft 推出 Oracle Exadata 数据库服务
- Oracle Database@Azure 推进到南美等新区域并增加了新服务
- Oracle宣布推出 Oracle Database@AWS 的有限预览版
- Oracle与Nextcloud合作,推出主权云上的安全协作平台
- NodeRED魔改版连接MsSql、PostgreSQL、MySQL、OracleDB存储无忧
- 对于企业数据云备份,“多备份”承诺的是成本更低,管理更高效#36氪开放日深圳站#
- 解读丨《归档文件整理规则》— 电子文件元数据存储
- Data Guard跳归档恢复的实践(dataguard failover)
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端路由 (61)
- 前端数组 (73)
- 前端js面试题 (50)
- 前端定时器 (59)
- 前端获取当前时间 (50)
- Oracle RAC (76)
- oracle恢复 (77)
- oracle 删除表 (52)
- oracle 用户名 (80)
- oracle 工具 (55)
- oracle 内存 (55)
- oracle 导出表 (62)
- oracle约束 (54)
- oracle 中文 (51)
- oracle链接 (54)
- oracle的函数 (57)
- 前端调试 (52)
本文暂时没有评论,来添加一个吧(●'◡'●)