js内存管理机制
JavaScript是在创建变量(对象,字符串等)时自动进行了分配内存,并且在不使用它们时“自动”释放。 释放的过程称为垃圾回收。
js存储
JavaScript中基础类型是保存在栈(stack)中的,会自动进行回收;而复合类型是保存在堆(dump)中的,通过 GC 操作进行空间释放。
值的初始化
JavaScript 在定义变量时就完成了内存分配。
1 | var n = 123; // 给数值变量分配内存 存在栈中 |
通过函数调用分配内存
有些函数调用结果是分配对象内存:
1 | var d = new Date(); // 分配一个 Date 对象 |
有些方法分配新变量或者新对象:
1 | var s = "azerty"; |
使用值
使用值的过程实际上是对分配内存进行读取与写入的操作。读取与写入可能是写入一个变量或者一个对象的属性值,甚至传递函数的参数。
当内存不再需要使用时释放
高级语言解释器嵌入了“垃圾回收器”,它的主要工作是跟踪内存的分配和使用,以便当分配的内存不再使用时,自动释放它。
垃圾回收
垃圾回收算法主要依赖于引用的概念。在内存管理的环境中,一个对象如果有访问另一个对象的权限(隐式或者显式),叫做一个对象引用另一个对象。
引用计数器算法
引用计数器是最初级的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。该算法有个限制:无法处理循环引用的事例。
标记-清除算法
这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。这个算法假定设置一个叫做根(root)的对象(在 Javascript 里,根是全局对象)。垃圾回收器将定期从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和收集所有不能获得的对象。
从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。所有对JavaScript垃圾回收算法的改进都是基于标记-清除算法的改进,并没有改进标记-清除算法本身和它对“对象是否不再需要”的简化定义。
js内存溢出的几种情况
- 给DOM对象添加的属性是一个对象的引用
1 | var MyObject = {}; |
解决方法:
在window.onunload事件中写上:
1 | document.getElementById('myDiv').myProp = null; |
- DOM对象与JS对象相互引用
1 | function Encapsulator(element) { |
解决方法:
在onunload事件中写上:
1 | document.getElementById('myDiv').myProp = null; |
- 给DOM对象用addEventListener绑定事件
1 | function doClick() {} |
解决方法:
在onunload事件中写上:
1 | element.removeEventListener('onclick', doClick); |
- 从外到内执行 appendChild 这时即使调用 removeChild 也无法释放
1 | var parentDiv = document.createElement("div"); |
解决方法:
从内到外执行appendChild:
1 | var parentDiv = document.createElement("div"); |
- 反复重写同一个属性会造成内存大量占用
1 | for(i = 0; i < 5000; i++) { |