漫谈内存泄漏问题
发布于 6 年前
约 2 分钟阅读
在实现 Stack 数据结构的时候使用到了 WeakMap 来实现私有化。
使用 Map 其实也能实现私有化,但是 Map 存在内存泄露的风险。
WeakMap 与 Map 唯一的区别就是弱引用。
弱引用
WeakMap 的键只能以引用类型作为键,并且这个键是弱引用的。
弱引用的对象不参与引用计数。当 GC 发生时,若对象不存在强引用,该对象便会被回收。
Map 内存分析
let Stack = (function () {
let map = new Map()
class Stack {
constructor() {
this.dep = new Array(5 * 1024 * 1024)
map.set(this, [])
}
}
return Stack
}())
function logMemoryUsed() {
console.log(`${process.memoryUsage().heapUsed / 1024 / 1024 >> 0} MB`)
}
global.gc()
logMemoryUsed()
let stack = new Stack()
global.gc()
logMemoryUsed()
stack = null
global.gc()
logMemoryUsed()
使用 node --expose-gc index.js
,输出
3 MB
43 MB
43 MB
Map 中的键为强引用,当 stack 置空后,map 仍持有 stack 对应的对象,使得内存空间不能被 GC 回收。而 map 是立即执行函数中的局部变量,外部访问不到,故使用 Map 实现私有化存在内存泄漏的风险。
关于数组占用空间说明
JS 中使用的是双精度浮点数,占用 8 个字节 64 位。数组默认分配一个元素的内存空间是 8 个字节。所以 5 M 长度的数组,占用空间为 40 M。
WeakMap 内存分析
let Stack = (function () {
let weakMap = new WeakMap()
class Stack {
constructor() {
this.dep = new Array(5 * 1024 * 1024)
weakMap.set(this, [])
}
}
return Stack
}())
function logMemoryUsed() {
console.log(`${process.memoryUsage().heapUsed / 1024 / 1024 >> 0} MB`)
}
global.gc()
logMemoryUsed()
let stack = new Stack()
global.gc()
logMemoryUsed()
stack = null
global.gc()
logMemoryUsed()
使用 node --expose-gc index.js
运行该程序,输出
3 MB
43 MB
3 MB
weakMap 中的键为弱引用,当键的强引用计数为零的时候,对象会被 GC 回收。
引用计数法和标记清除算法
let Stack = (function () {
let map = new Map()
class Stack {
constructor() {
this.dep = new Array(5 * 1024 * 1024)
map.set(this, [])
}
}
return Stack
}())
function logMemoryUsed() {
console.log(`${process.memoryUsage().heapUsed / 1024 / 1024 >> 0} MB`)
}
global.gc()
logMemoryUsed()
let stack = new Stack()
global.gc()
stack = null
global.gc()
setInterval(() => {
global.gc()
logMemoryUsed()
}, 5000)
输出
3 MB
43 MB
43 MB
43 MB
43 MB
43 MB
43 MB
43 MB
43 MB
43 MB
43 MB
43 MB
3 MB
3 MB
由上可知,内存在大约 1 分钟后将 map 和实例对象回收了。 map 属于实例对象的成员变量,而 map 中引用了实例对象,产生了循环引用。按照引用计数法两个对象都不能被 GC 回收。 引用计数法是最初级的垃圾回收机制。从 2012 年开始,所有的现代浏览器都是使用了标记清除算法。 标记清除算法会从全局对象开始查询,无法查询到的对象都将被清除,那么那些循环引用的对象也将被清除。
参考
- 弱引用
- Map 内存分析
- 关于数组占用空间说明
- WeakMap 内存分析
- 引用计数法和标记清除算法
- 参考
除特别注明外,所有文章均采用 Creative Commons BY-NC-ND 4.0(自由转载-保持署名-非商用-禁止演绎)协议 发布