JavaScript 内存管理 & 垃圾回收机制
标记清除
js 中最常用的垃圾回收方式就是标记清除。当变量进入环境时,例如,在函数中声明一个变量,就将这个而变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。
引用计数
这是最简单的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。
Netscape Navigator3 是最早使用引用计数策略的浏览器,但很快它就遇到了一个严重的问题:循环引用。循环引用指的是对象 A 中包含一个指向对象 B 的指针,而对象 B 中也包含一个指向对象 A 的引用。
1
2
3
4
5
6
7
|
function fn() {
var a = {};
var b = {};
a.pro = b;
b.pro = a;
}
fn();
|
以上代码a和b的引用次数都是2,fn()执行完毕后,两个对象都已经离开环境,在标记清除方式下是没有问题的,但是在引用计数策略下,因为a和b的引用次数不为0,所以不会被垃圾回收器回收内存,如果fn函数被大量调用,就会造成内存泄漏。在IE7与IE8上,内存直线上升。
最简单的方式就是自己手工解除循环引用,比如刚才的函数可以这样
1
2
|
myObject.element =
null;
element.o =
null;
|
内存管理
1、什么时候触发垃圾回收?
垃圾回收周期性运行,如果分配的内存非常多,那么回收工作也会很艰巨,确定垃圾回收时间间隔就变成了一个值得思考的问题。IE6 的垃圾回收是根据内存分配量运行的,当环境中存在 256 个变量、4096 个对象、64K 的字符串任意一种情况的时候就会触发垃圾回收器工作,看起来很科学,不用按一段时间就调用一次,有时候会没必要,这样按需调用不是很好嘛?但是如果环境中就是有这么多变量一直存在,现在脚本如此复杂,很正常,那么结果就是垃圾回收器一直在工作,这样浏览器就没法玩了。
微软在 IE7 中做了调整,触发条件不再是固定的,而是动态修改的,初始值和IE6相同,如果垃圾回收器回收的内存分配量低于程序占用内存的 15%,说明大部分内存不可被回收,设的垃圾回收触发条件过于敏感,这时候把临界条件翻倍,如果回收的内存高于 85%,说明大部分内存早就该清理了,这时候把触发条件置回。这样就使垃圾回收工作智能了很多。
2、合理的 GC 方案
1)、JavaScript 引擎基础 GC 方案是(simple GC):mark and sweep(标记清除),即:
- 遍历所有可访问的对象。
- 回收已不可访问的对象。
2)、GC 的缺陷
和其他语言一样,JavaScript 的 GC 策略也无法避免一个问题:GC 时,停止响应其他操作,这是为了安全考虑。而 JavaScript 的 GC 在 100ms 甚至以上,对一般的应用还好,但对于 JS 游戏,动画连贯性要求比较高的应用,就麻烦了。这就是新引擎需要优化的点:避免 GC 造成的长时间停止响应。
总结
一般不用 setInterval,而用 setTimeout 的延时递归来代替 interval。
setInterval 会产生回调堆积,特别是时间很短的时候。
扩展
setInterval 有个很烦的地方就是当 js 主程序空闲的时候,执行代码队列里面的代码的时候,如果此时候我们有一个问题,定时器是等到回调执行完,才开始计时进行下次循环呢?还是只要一次计时完毕,插入回调之后不管回调执不执行就开始计时呢?答案显示是后者,这也就是我说 setInterval 坑的原因啊,因为这会出现一种情况,当我们插入回调的时候前队列有别的代码在执行,这时候回调肯定是不会执行的,因此如果这个时候无限定时时间到了会再次插入回调,这个时候如果发现队列中的第一次回调没有执行,那么再次插入的回调浏览器就默认取消,(这是以防出现回调连续执行多次的情况)但是这又引发了新的情况就是有些回调是不能取消掉的?
本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/bonelee/p/8134748.html,如需转载请自行联系原作者