Javascript Memory Leak

困扰

IE memory leaks like a sieve(筛子) and my webpage is getting slower and slo...ower. But memory usage keeps climbing… I've tried everything including banging my head on the desk(猛敲桌子). It just doesn't change anything.

IE的内存泄露使得网页速度变得越来越慢。然后这不要紧,浏览器占用的内存一直在上升,我尝试了各种方法,甚至猛拍桌子,但都无济于事。

 

解释

The fact is that IE has separate garbage collecting mechanisms for COM interfaces and JavaScript objects and is unable to resovle circular references between DOM(or ActiveX or any kind of COM) objects and JavaScript objects. (原因是IE分离了COM接口和JavaScript对象相互之间的垃圾回收,所以不能解决DOM对象和javascript对象之间的闭包问题)When objects form the two worlds have circular references between them, GC can not detect them and the memory cannot be reclaimed until IE exists.(当DOM对象和JavaScript对象有闭包引用的时候,由它们创建的对象,垃圾回收机制不能检测到它们,所以除非不用IE,对象所占的内存不能被回收) There are several patterns that cause circular references.(造成闭包的原因由好几种,一种误解就是将内部函数作为事件的响应函数会造成内存泄露。) Unfortunately, assigning nested functions as event handlers falls into this category. IE is rejecting the use of closure, one of the most powerful and flexible feature of JavaScript.

(闭包是Javascript的一个很有用,而且很灵活的特征,但是IE不会回收闭包的使用带来的内存)

牢骚

It is frustrating to know that experts at Microsoft like Eric Lippert suggest "Don't use closures unless you really need closure semantics. In most cases, non-nested functions are the right way to go." It sounds like that programmers are abusing closures, completely ignoring that IE has a big problem with closures. It would be reliefing and reasonable to expect that IE will relaim leaked memory after a page has been unloaded. But that's not the fact. And here I found some explanation: "...the application compatibility lab discovered that there were actually web pages that broke when those semantics were implemented. (No, I don't know the details.) The IE team considers breaking existing web pages that used to work to be way, way worse than leaking a little memory here and there, so they've decided to take the hit and leak the memory in this case." I don't understand. Maintaining backward compatibility with rare web pages which even an scripting engine writer does not know much about at the cost of leaking memory possibly for all web pages? What kind of decision is it? They don't admit their own faults but instead suggest poor coding practices.

测试

But we have to code for IE, however buggy it is. I've run the test from Mihai Bazon(see IE: WHERE'S MY MEMORY?).

 
 

 

js 代码
  1. function createEl(i) {   
  2.     var span = document.createElement("span");   
  3.     span.className = "muci";   
  4.     span.innerHTML = " foobar #"+i+" ";   
  5.     span.onclick = function() { alert(this.innerHTML + "n" + i); };   
  6.     document.body.appendChild(span);   
  7. }function start() {   
  8.     var T1 = (new Date()).getTime(); // DEBUG.PROFILE   
  9.     for (var i = 0; i < 3000; ++i)   
  10.         createEl(i);   
  11.     alert(((new Date()).getTime() - T1) / 1000); // DEBUG.PROFILE   
  12. }  

The first request in IE took 1.797s, the tenth 8.063s. Memory usage kept growing. Firefox reports a typical value of 1.6xs with no memory leak. Prototype.js avoids IE memory leak by hooking window's unload event, unobserving all events and clearing its event handler cache. I replaced the line span.onclick = function() { alert(this.innerHTML + "n" + i); }; with Event.observe(span, 'click', function() { alert(this.innerHTML + "n" + i); }); and rerun the test. The good news is that the leaked memory in IE is reclaimed when the page unloads. The bad news is that each request takes approximately 17s in IE while Firefox only needs 2.1xs! The Prototype event system makes it possible to free memory when page unloads but is extremely slow and uses more memory in a single request. The speed degradation is explainable: Event._observeAndCache saves extra references thus uses more memory and IE gets slow as it leaks memory. Event.observe does more things than a simple assignment thus is much slower. However, memory leak is under control…

思考

I admire Edward Dean's addEvent though it doesn't solve the memory leak problem with closures. (Dean insisted that his script does not leak memory in comments. Maybe he is not talking about the closure case). Leak Free Javascript Closures solution can really prevent memory leak. The way it breaks cirular reference is create a new function which holds no references to the closing scope.

In the simplest case:

 
 

 

js 代码
  1. //See update below   
  2. //Function.prototype.closure = function() {   
  3. //    return function () {   
  4. //        return this.apply(null, arguments);   
  5. //    };   
  6. //};//update 2006-08-22 13:10:15 GMT+0800   
  7. //Holds references to functions   
  8. __funcs = [];//The code fragment is for demostrative purpose only and lacks of optimization.   
  9. //Do not use it in productive enviromnent!   
  10. Function.prototype.closure = function() {   
  11.     __funcs.push(this);   
  12.     return function () {   
  13.     return __funcs[__funcs.length - 1].apply(null, arguments);   
  14.     };   
  15. };   
  16. function setup() {   
  17.     var span = $('span1');   
  18.     span.bigProperty = new Array(1000).join('-_-');   
  19.     span.onclick = function() {alert(span.bigProperty.length);}.closure();   
  20. }   
  21. setup();  

This will not leak memory in IE. The nested function in setup() forms a closure so it's able access span. span.onclick does not refer to the nested function but a newly created function returned by the closure() method of the nested function. The newly created function invokes the nested function via global array __funcs and have no references to the scope of setup(). So there is no circular reference. You may argue that the newly created function is able to call the nested function, it must have some kind of reference to it while the nested function have reference to span via closure, so there will be a circular reference. However, ECMA 262 treat this as a keyword rather than an identifier, this keyword is resolved dependent on execution context in which it occurs, without reference to the scope chain (see Javascript Closures ). It means that this keyword is free of reference so it can be cleverly used to break circular references. This is a hack indeed. IE always needs hacks to amend its holes.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值