前些天在javaeye上发了一个帖子,“动态加载Javascript会导致内存泄露?”,自己当时被老外的那篇文章搞的比较困惑,网上也请教了一些高人,这里要非常感谢那些帮助我的人,工作非常繁忙的时候还愿意给我讲解他们对问题的看法,真的让我很感动!
这里再介绍一个比较好用的工具:Drip/sIEve,感谢jindw为我推荐这个工具,Drip和sIEve的作者应该是同一个人,连open souce上的项目都是在一起的。不过,平时我们主要使用sIEve就可以了,sIEve的功能比Drip强大一些,sIEve 是一个帮助我们查看ie浏览器(他本身基于操作系统安装的ie内核)内存的使用和内存泄露问题的。具体使用很简单,这里再提供一个链接给大家:http://home.orange.nl/jsrosman/,大家下载下来后就可以使用了。
Firefox下面也有一个检查内存泄露的工具,不过我用过,感觉有时候很让人抓狂,大家有兴趣的不妨也试试。PuterJam为我们总结了一篇“javascript 内存泄露工具使用”,推荐去看看。
来看我做的测试,代码1:
<body>
<script type="text/javascript">
script = document.createElement('script');
script.src = 'little.js';
script.id = 'JSONP';
script.type = 'text/javascript';
script.charset = 'utf-8';
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
</script>
测试页面2
<script type="text/javascript">
var script = document.getElementById('JSONP');
script.parentNode.removeChild(script);
</script>
</body>
在sIEve的结果是:
可以看到,id=JSONP的节点是孤立状态,而没有被回收 (还有一个孤立节点 大家不用管) ,为了解决这个办法,从EXTjs里面的代码得到灵感:
我把页面代码修改成:
<body>
<script type="text/javascript">
script = document.createElement('script');
script.src = 'little.js';
script.id = 'JSONP';
script.type = 'text/javascript';
script.charset = 'utf-8';
var body = document.getElementsByTagName('body')[0];
body.appendChild(script);
</script>
测试页面2
<script type="text/javascript">
var script = document.getElementById('JSONP');
var d = document.createElement('div');
d.appendChild(script);
d.innerHTML = '';
</script>
</body>
可惜的是竟然还是存在没被回收的节点,囧!:
这里我暂时还不知道是什么原因,没仔细研究下去,如果是div的话,用这种方式是可以把节点回收的。
克军大牛跟我讲,“再加一行script = null;确实jsonp不能滥用,…,一般解决dom的循环reference就是null掉”,于是我把最后的script对象null掉,发现果然节点不存在了!
Code:
<body>
<script type="text/javascript">
script = document.createElement('script');
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
script.src = 'little.js';
script.id = 'JSONP';
script.type = 'text/javascript';
script.charset = 'utf-8';
</script>
测试页面2
<script type="text/javascript">
var script = document.getElementById('JSONP');
script.parentNode.removeChild(script);
script = null;
</script>
</body>
结果图:
看来还是不能想什么奇淫技巧,老老实实把最后的对象引用null掉
ranklau那天抽出时间跟我讲:“…如果用htmlelement,remove后会泄露。用fragment,fragment不用append到DOM上,不会有泄露。一个节点append到dom上,你再移除它就会有泄露的。所以才会出现象ext的trash机制…”,ranklau一看就是高手,我也是陋闻了,这些结论竟然都不知道!不过ranklau也是技术人员一贯的严谨作风,自己没去做测试,所以希望我能去做测试验证这些观点。今天上网找到了这方面的一个帖子,发现“这是IE的一个特点:document fragment不append到document的dom里面的时候,也可以拥有自己的脚本运行空间,可以用script标签发起请求。这样用document fragment就可以比iframe使用更少的客户端资源来完成操作。”好!做测试:
<body>
<script type="text/javascript">
var df = document.createDocumentFragment();
script = document.createElement('script');
script.src = 'little.js';
script.id = 'JSONP';
script.type = 'text/javascript';
script.charset = 'utf-8';
df.appendChild(script);
</script>
测试页面2
<script type="text/javascript">
var script = document.getElementById('JSONP');
alert(script);
</script>
</body>
测试结果:
id=JSONP的节点至始至终没有append到页面的dom树上来,是一个孤立的点,但是,null掉后也就被回收了,所以,这种方式也不错!
最后,我却怀疑这个documentFragment会引起内存泄漏,因为浏览器要为这个新节点产生一个新的脚本scope,所以肯定会消耗额外的内存,至于到底是不是这样,还是测试:
Code:
<body>
<script type="text/javascript">
for(var i=0;i<100;i++){
var dom = document.createDocumentFragment();
script = document.createElement('script');
script.src = 'little.js';
script.id = 'JSONP';
script.type = 'text/javascript';
script.charset = 'utf-8';
dom.appendChild(script);
dom.removeChild(script);
script=null;
}
</script>
</body>
如图,发现,还是存在额外的内存消耗
最后,看网上有人说“IE8beta版本不支持documentFragment拥有独立的运行空间,正式版又解决了这个问题”,这个我不确定,因为我至今没装IE8,大家有兴趣可以去测试下,不过我觉得比较稳妥的方式还是最好别用documentFragment。
转自:http://04js.cn/content.asp?id=1613
今天自己再次做了一些测试,和上次不一样,这次集中在IE上面(上次在firefox测试比较多),这几天的请教让我明白:IE与firefox、chrome的解析机制是不一样的,“对于需要频繁动态加载的script(比如跨域的jsonp接口),可以对ie做一下专门的优化,因为ie里Dom解析机制是c++写的,好像属于windows网络堆栈的那个部分,跟Javascript引擎的垃圾回收机制完全不同,内存泄漏问题比较明显……”,“firefox的内存GC机制比IE强悍,不必作为重点考虑对象。只要ie的问题能处理好,firefox上基本上也没有什么问题。”