Chinaunix首页 | 论坛 | 博客
  • 博客访问: 868816
  • 博文数量: 366
  • 博客积分: 10267
  • 博客等级: 上将
  • 技术积分: 4290
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-24 14:04
文章分类

全部博文(366)

文章存档

2012年(366)

分类: 系统运维

2012-05-08 15:50:16

随 着移动互联网的发展,现在越来越多的应用开始想bs方向转移,原来很多复杂的计算逻辑也自然随着转移到了客户端,需要客户端的javascript来担任 实现的角色;原本不起眼的内存泄露,现在却犹如恶魔会很快的吞噬掉用户的内存。从去年开始就在专注js面向对象开发实现、托管代码垃圾回收算法、js内存 泄露相关的东西;如果大家关注过这方面的东西,那我想你一定读过微软开发人员Justin Rogers撰写的那篇经典的内存泄露模式,其实道理阐述的很清楚,但是怎么证明和呈现泄露的存在呢?这是一直困扰我的一个难题!今天我们针对这篇文章的 各个模式,记录一下自己的一些想法,很多是自己的推论,并不一定正确,欢迎大家批评指正;

         泄露的客观因素

       在过去,对于web开发人员内存泄露并没有引起大的问题,主要是因为页面都相对比较简单,并且页面也只是在一个站点内跳转(这也是一种很好的释放松散内存的方式);如果有内存泄露的话,那也是小的不足以引起开发人员的注意。

       随着技术的发展,新的应用程序需要遵循更高的标准。一个页面或许会运行数个小时而没有跳转页面并通过web服务动态检索数据进行更新。复杂的事件机制、面 向对象开发、闭包等很多语言特性被用来开发整个应用程序。在这些和其他的一些改变下,某些内存泄露模式开始变得更加突出,尤其是那些以前被页面跳转隐藏的 泄露问题。

       内存泄露模式

       接下来我们将会讨论内存泄露模式,并且每个模式都会给出一些简单的例子。现在,我们简单的看一下以下的模式:

1.       循环引用---当IE中COM对象和js的对象形成循环引用的时候,对象得不到释放就是造成内存泄露。这是一种最广泛的模式。

2.       闭包---作为一种在web应用程序中使用最广泛的模式,闭包是以一种特殊方式的循环引用造成内存泄露的。

3.       跨页泄露---当你从一个页面跳转到另一个页面的时候,由于产生的一些中间对象会形成很微弱的内存泄露。

4.       Pseudo-Leaks---这个并不是真正的泄露,但是如果你不理解这个,那么你的内存不断增加也是十分恼人的。

以上是根据自己对文章的理解,对部分文字的翻译,下面我们将以同样的方式,先就翻译,然后记录相应的思考。

循环引用模式

循环引用几乎是所有泄露的根源。正常情况下,js的脚本引擎是可以通过垃圾回收器解

决 循环引用问题的(js的垃圾回收器使用的标记擦除法,前面已经介绍过标记擦除法,这种算法本身是可以解决循环引用的问题),但是某些未知的情况会阻止其正 常工作。在IE中这些未知情况出现在js对象可以访问DOM元素的情况下。其基本的规则原理如下图(循环引用模式原理图)所示

         该模式导致内存泄露的原因是基于COM的引用计数器。Js引擎引用一个DOM元素,那么回收和释放DOM元素的引用,只能一直等待到对其引用被解除。在我 们的图中,js引擎存在两个引用:scope对象和DOM元素的expando属性。当js引擎结束的时候将会释放第一个引用,但是对DOM元素的引用并 不会释放,因为它会一直等待js引擎释放对它的引用。你或许会想很容易就可以探测到这种情况并解决这个问题,但是在实践中这只是众多情况中的冰山一角。如 果循环引用是由30个对象的环(类似链表)形成的,那么将会很难探测到。


图 1. 循环引用模式原理图

 

根据这个原理图,我写了个测试的例子,在函数principlePictureLeak中,我们获取到span元素,并使obj1指向它,然后实例化对象obj4,并使span元素的expando属性指向obj4,具体的代码如下        

View Code 

         在内存泄露检测工具sIEve中运行以上代码,我们可以看到并没有内存泄露,具体信息详见 下图(循环引用原理图内存泄露检测图) 

                

 

图 2. 循环引用原理图内存泄露检测图

 

         那么为什么没有形成内存泄露呢?按照Justin Rogers对循环引用模式原理的解释,上边的图1并没有完全展示出其描述的对象关联关系,那么我们可以看一下这些对象之间的关系(如图3),从图中我们 可以看到对象图中并没有形成真正的”圆环”,自然也就没有形成真的循环引用,在函数执行完以后,js引擎就会释放对方法scope对象的引用,然后 scope对象就会释放对obj1、obj4的引用,然后obj1就会释放对DOM元素的引用;那么这个时候如果执行完函数后,obj4是否会释放内存 呢?


图 3. 循环引用模式原理图的对象关系

那 么我们先来看以下的代码测试,其意图就是在setReference里使span元素引用一个js的对象,然后在getReference里来获取这个对 象并输出其属性msg的值,经测试其最终输出了“DOM引用了js对象”,那么我们可以知道,DOM应用的局部变量并不会因为函数执行完毕而被回收掉。

View Code 

 

通 过以上的代码测试,principlePictureLeak执行完成后,并没有回收obj4对象,但是当我们在sIEve中重复刷新页面并没有泄露,那 么也就证明了,在刷新页面的时候,js脚本引擎解除了对DOM的引用,当DOM被回收后,就会释放对obj4的引用,从而成功的回收obj4。

以下是Justin Rogers针对循环引用模式给出的一个例子,代码如下

View Code 

 

 

我们可以简单的看以下各对象之间的引用关系(如图4),从图中我们可以看到myGlobalObject和LeakedDiv之间形成了循环引用的”环路”,这个在理论上构成了内存泄露的条件,那么到底有没有泄露呢,让我们使用sIEve来测试一下吧。

 

 

图 4. 循环引用例子1对象关系图

 

         测试结果如下图(图5),让我们跌破眼睛的事情发生了,我们期盼已久的内存泄露并没有出现。那么这又是什么原因呢?那么下边我们看一下我这个可以出现内存 泄露的代码例子。从代码中我们可以看到唯一不同的地方,就是我们新增的DOM元素,而不是直接使用的页面中的DOM元素。

        

View Code 

 

 

        

        

 

图 5. 循环引用例子泄露测试结果

 

         先来看一下sIEve的测试结果吧,如图6,从图中我们可以看到,随着页面的刷新,内存泄露的数目不断增加,通过对比,我们可以知道循环引用模式产生内存泄露除了满足循环引用外,其中的DOM是动态新增的,而不是页面原有的DOM!

 

 

图 6. 我的循环引用例子泄露测试结果

 

         最后总结一下,循环引用模式产生内存泄露除了满足循环引用外,其中的DOM是动态新增的,而不是页面原有的DOM!测试环境window 7 + IE 8

阅读(1411) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~