study
study copied to clipboard
内存泄漏与垃圾回收机制
内存生命周期
不管什么程序语言,内存生命周期基本是一致的:
- 分配你所需要的内存
- 使用分配到的内存(读、写)
- 不需要时将其释放 \ 归还
在所有语言中第一和第二部分都很清晰,最后一步在底层语言中很清晰,但是在像 JavaScript 等上层语言中,这一步是隐藏的、透明的。
什么是内存泄漏?
内存不再被需要(使用)但没有被释放(回收)
在 JavaScript 这门高级语言中,内存管理依赖于 JS 引擎的垃圾回收机制。
什么是垃圾回收机制?
跟踪内存的分配和使用,当分配的内存不再使用时,自动释放它。
这只能是一个近似的过程,因为要知道是否仍然需要某块内存是无法判定的(无法通过某种算法解决)。
垃圾回收算法
-
引用计数 - 对象是否被引用
最简单的垃圾收集算法,收集并记录对象被引用的次数,当对象引用次数为 0 时被回收。
此算法的缺陷是无法处理循环引用:
*** title *** function foo () { var oa = {} var ob = {} oa.b = ob // oa 引用 ob ob.a = oa // ob 引用 oa return 'azerty' } foo()
上例中,两个对象被创建,并互相引用,形成了一个循环。它们被调用之后不会离开函数作用域,所以它们已经没有用了,可以被回收了。然而,引用计数算法考虑到它们互相都有至少一次引用,所以它们不会被回收。
-
引用标记 - 对象是否可获取
这个算法假定设置一个叫做根(root)的对象(在 JavaScript 中,根是全局对象)。定期的,垃圾回收器将从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象。
此算法解决了循环引用的问题,上例中 foo 函数执行完毕后,全局对象将无法获取 foo 函数内定义的两个对象。
此算法也有它的限制:那些无法从根对象查询到的对象都将被清除,不过无需担心,实践中我们很少会碰到类似的情况。
从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。所有对 JavaScript 垃圾回收算法的改进都是基于标记 - 清除算法的改进,并没有改进标记 - 清除算法本身和它对“对象是否不再需要”的简化定义。
常见的内存泄漏和避免方式
内存泄漏识别方式
文中使用 chrome 开发者工具 timeline 面板查看内存占用方式需更改为 Performance 面板,并勾选 Memory 选项。