31 七月 2009 @ 11:13 下午 

前端程式越搞越複雜,接踵而至的問題越來越多,雖然瀏覽器的效能不斷地提昇,但是只要IE6沒被淘汰的一天,網頁程式設計師永遠要替IE6補洞,Memory Leak是一個非常難搞得議題。關於微軟的官方說法可以參考Understanding and Solving Internet Explorer Leak Patterns,裡頭有詳細說明IE6會如何發生memory leak的情況。發生memory leak的徵狀就是:記憶體不斷飆上去,就算reload頁面,記憶體也不會被釋放,除非關掉瀏覽器。

在此不介紹為何為引發memory leak,因為我覺得這是瀏覽器應該要處理的問題,要拿出來講只是因為IE6沒辦法處理好罷了,雖然其他瀏覽器也並不是沒有這個問題,但是至少問題都沒有這麼嚴重,因此只需要針對IE6來處理即可。以下是我整理出幾種比較好的處理方式:

  1. Douglas Crockford針對memory leak的議題發表一篇文章:JScript Memory Leaks提出一個purge的function,概念非常簡單,就是遍歷元素底下的所有節點,將所有的function設為null(這屬於closure的循環參照索引發的memory leak)。一般來說可以在unload時或者你要removeNode之前來做這個動作,例如:
    function purge(d) {
        var a = d.attributes, i, l, n;
        if (a) {
            l = a.length;
            for (i = 0; i < l; i += 1) {
                n = a[i].name;
                if (typeof d[n] === 'function') {
                    d[n] = null;
                }
            }
        }
        a = d.childNodes;
        if (a) {
            l = a.length;
            for (i = 0; i < l; i += 1) {
                purge(d.childNodes[i]);
            }
        }
    }
    window.onunload = function(){
        purge(document.body);
    }
    
  2. Event Cache :若你的專案沒用到一些framework時,可能會用到一些自訂的addEvent方法,這時可以透過EventCache.add來將事件加入cache,然後在unload時做EventCache.flush。這樣可以避免在unload時循環整個body,雖然降低了memory leak,但卻吃掉cpu的效能。
    var EventCache = function(){
     var listEvents = [];</p>
    <p> return {
     listEvents : listEvents,</p>
    <p> add : function(node, sEventName, fHandler, bCapture){
     listEvents.push(arguments);
     },</p>
    <p> flush : function(){
     var i, item;
     for(i = listEvents.length - 1; i >= 0; i = i - 1){
     item = listEvents[i];</p>
    <p> if(item[0].removeEventListener){
     item[0].removeEventListener(item[1], item[2], item[3]);
     };</p>
    <p> /* From this point on we need the event names to be prefixed with 'on" */
     if(item[1].substring(0, 2) != "on"){
     item[1] = "on" + item[1];
     };</p>
    <p> if(item[0].detachEvent){
     item[0].detachEvent(item[1], item[2]);
     };</p>
    <p> item[0][item[1]] = null;
     };
     }
     };
    }();
    
  3. 利用try…finally來清除不被引用的參考:這常會發生在我們利用function來creat Element時,忽略了不再被引用的參考,然後直接return,在IE6中會引發memory leak,這篇文章透過try,finally來清除不被引用的參考,例如:
    function createButton() {
          var obj = document.createElement("button");
          obj.innerHTML = "click me";
          obj.onclick = function() {
            //handle onclick
          }
          obj.onmouseover = function() {
            //handle onmouseover
          }</p>
    <p>      //this helps to fix the memory leak issue
          try {
            return obj;</p>
    <p>      } finally {
            obj = null;
          }
        }
    
  4. innerHTML所引發的memory leak:這個議題最為常見,innerHTML不被W3C列為標準,但因為他比起操作DOM元素容易的多了,因此廣泛的被使用,但是之前有提到,最好是在removeNode時做一次檢查,將node裡頭的function全部設為null,以避免closure所引發的memory leak,但是innerHTML實在太方便了,讓所有programer將這個問題拋在腦後,因為我隨時可以設定某個元素的innerHTML,哪怕他裡頭又肥又大,一句innerHTML就可以改變DOM元素。文章裡頭的例子用YUI來做示範,不過事實上,他還是用了Douglas了perge來清除循環引用。
    YAHOO.util.Dom.setInnerHTML = function (el, html) {
        el = YAHOO.util.Dom.get(el);
        if (!el || typeof html !== 'string') {
            return null;
        }</p>
    <p>    // Break circular references.
        (function (o) {</p>
    <p>        var a = o.attributes, i, l, n, c;
            if (a) {
                l = a.length;
                for (i = 0; i < l; i += 1) {
                    n = a[i].name;
                    if (typeof o[n] === 'function') {
                        o[n] = null;
                    }
                }
            }</p>
    <p>        a = o.childNodes;</p>
    <p>        if (a) {
                l = a.length;
                for (i = 0; i < l; i += 1) {
                    c = o.childNodes[i];</p>
    <p>                // Purge child nodes.
                    arguments.callee(c);</p>
    <p>                // Removes all listeners attached to the element via YUI's addListener.
                    YAHOO.util.Event.purgeElement(c);
                }
            }</p>
    <p>    })(el);</p>
    <p>    // Remove scripts from HTML string, and set innerHTML property
        el.innerHTML = html.replace(/<script[^>]*>[\S\s]*?<\/script[^>]*>/ig, "");</p>
    <p>    // Return a reference to the first child
        return el.firstChild;
    };
    

大概是這樣了,memory leak實在是個很惱人的議題,不過有責任感的前端工程是還是要將之視為己任的,畢竟項目越做越大,然而記憶體越吃越兇,這是很可怕的一件事。

Posted By: BLACKBING
Last Edit: 02 八月 2009 @ 03:42 下午

EmailPermalink
Tags


 

Responses to this post » (None)

 
Post a Comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>


 Last 50 Posts
Change Theme...
  • Users » 17
  • Posts/Pages » 59
  • Comments » 28
Change Theme...
  • VoidVoid « Default
  • LifeLife
  • EarthEarth
  • WindWind
  • WaterWater
  • FireFire
  • LightLight

About Me



    No Child Pages.

留言板



    No Child Pages.

Album



    No Child Pages.