backup2007

          導(dǎo)航

          <2009年2月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          1234567

          統(tǒng)計

          公告

          @import url(http://www.aygfsteel.com/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);


          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          我所理解的IE內(nèi)存泄露

          我所理解的IE內(nèi)存泄露

                                     

          最近在做一些web方面的東西,突然發(fā)現(xiàn)IEremoveChild一個元素并釋放了其dom對象的引用之后,在任務(wù)管理器中并不能將內(nèi)存釋放. 這讓我相當(dāng)郁悶, Google一翻,各種內(nèi)存泄露的說法,看的我頭暈。最終還是有些收獲。

          測試1

           

          1.不泄露!

          function test2(){

                 gb = document.getElementById('garbageBin');

                 var i = 0;

                 while(i++<10000){

                        var o = document.createElement("<div onclick='foo();'>");

                        gb.appendChild(o);

                        gb.removeChild(o);

                 }    

          }

           

          2 或者當(dāng)即removeChild, 而是過后在另外一個函數(shù)中 gb.innerHTML=””

          也不泄露。也就是,在刷新之前,可以把內(nèi)存釋放出來。

           

          3 如果即不remove又不 gb置空, 則刷新也要不回內(nèi)存.

           

          4. 如果只是var o = document.createElement("<div onclick='foo();'>"); 其他什么也不做,

          同樣導(dǎo)致泄露。

           

          以上測試在IE7IE6等會再說。 這告訴我們,如果你在創(chuàng)建元素的時候已經(jīng)關(guān)聯(lián)了事件處理對象,也就是內(nèi)聯(lián)的foo(), 那么一定要將元素掛載到頁面中的dom樹上, 這樣你才有可能通過remove或者innerHTML=’’的方式回收內(nèi)存。

           

          我到現(xiàn)在對于內(nèi)存泄露還有疑惑,到底怎樣才算泄露?

          是刷新頁面之前可以把內(nèi)存收回才算不泄露,還是刷新之后可以收回內(nèi)存就算不泄露?

          前輩們給出的MSDN上那個父子div插入順序的例子, 我在IE7中跑, 似乎不像網(wǎng)上說的那樣,結(jié)果是有泄露的那個函數(shù),運行完刷新瀏覽器,內(nèi)存被回收了。而第二個函數(shù)內(nèi)存一直沒長。 這樣理解的話,應(yīng)該是瀏覽器刷新之前就被回收(不上漲)就算不泄露吧。那么刷新之后還無法收回的算什么呢?更厲害的內(nèi)存泄露?呵呵。我暈了。

           

          暫且認(rèn)為:如果在瀏覽器刷新之前, 內(nèi)存可以被回收,才算不泄露。

          得出:

          規(guī)則1:創(chuàng)建的元素,如果包含內(nèi)聯(lián)腳本(這是這條規(guī)則的前提情況)一定要掛載在dom樹上!不能先掛載到不在樹上的元素!

          如果不包含內(nèi)聯(lián)腳本對象, 則不會泄露。

          如果包含了內(nèi)聯(lián)腳本,則記得先掛在樹上~ 哈哈 也就是先把父節(jié)點掛載,然后掛載子節(jié)點到父節(jié)點.

          再者說來,在FF等其他瀏覽器中,根本不允許
          document.createElement("<div onclick='foo();'>");
          這樣的代碼出現(xiàn)么!
          所以按照標(biāo)準(zhǔn)的寫法來做是有好處的!

          var o =document.createElement(“div”)

          o.onclick = foo;

          這樣就不會有泄露!

           

          規(guī)則2  不討論內(nèi)聯(lián)事件對象的情況,因為那是個特例, 來看這樣的情況

          function test(){

                 gb = document.getElementById('garbageBin');

                 var i = 0;

                 while(i++<5000){

                        (function(){

                        var o = document.createElement("div");

                        //產(chǎn)生循環(huán)引用!

                        o.onclick = function(){

                               alert('haha')

                        }

                       

                        gb.appendChild(o)

                       

                        //雖然remove了元素,由于循環(huán)引用的存在,無法回收內(nèi)存

                        //應(yīng)當(dāng)在remove之前,打破循環(huán)引用!

                        //o.onclick = null; //break!

                        gb.removeChild(o)

                       

                        })();

                 }

          }

          注意哈,這此會導(dǎo)致內(nèi)存泄露,就像前輩們說的那樣, o.onclick = function一句產(chǎn)生了dom元素和function對象的循環(huán)引用。就算掛載到dom,然后remove,內(nèi)存依然不能收回。(似乎能收回一些,但是仍然沒有回到調(diào)用前的數(shù)值), PF使用率成上升趨勢.
          當(dāng)然解決方法很簡單, 只要在 removeChild 之前將循環(huán)打破即可. 使用o.onclick = null 即可,或者事件響應(yīng)寫在函數(shù)外面。這個網(wǎng)上有很多講,不多說。我關(guān)注的其實不是這個,主要是, IE7removeChild到底能做些什么? 刷新頁面的時候,回收的又是哪些內(nèi)存?

          (如果不append也不remove,則會徹底的泄露,刷新無濟(jì)于事.)

          我將紅色的部分,也就是內(nèi)部包裝的function去掉。循環(huán)5000次,內(nèi)存沒有增長! 我做這樣的猜測:內(nèi)存泄露是由于循環(huán)引用沒錯,然而onclick 關(guān)聯(lián)的那個function 屬于匿名函數(shù)哈,在整個過程中只解析一次,因此無論你循環(huán)多少次,泄露的都只是跟這一個對象有關(guān),因此泄露數(shù)量很少很少,以至于看不出。

          然而包裝成函數(shù)調(diào)用5000次呢?就生成了5000onclick關(guān)聯(lián)的function, 泄露的內(nèi)容就增大了5000倍,因此我們可以看的出來。

           

          3

          下面測試正常的創(chuàng)建元素。 先測試只創(chuàng)建, append到樹上的情況.

          //IE7下沒有問題,內(nèi)存不會增長,說明IE7可以動態(tài)回收不在結(jié)點樹上的并且沒有關(guān)//聯(lián)JS對象, 且沒有其他對象引用它的元素.

          function test2(){

                 var i = 0;

                 while(i++<5000){

                        //(function(){

                               var o = document.createElement("<div>");

                               o.innerHTML = "AAA";

                               //加上下面兩句也沒問題!

                               buf.push(o);//如果后面不做處理,則不會被回收哦

                               buf.pop();   //直接pop掉,也就沒有對象引用這個元素了       

           

                        //})();

                        //buf.pop 放到這里也完全OK,內(nèi)存不會增長

                 }

                

          }

           

           

          也就是說,定義一個dom元素,只要沒有循環(huán)引用, appenddom樹上也沒問題。只要沒有被引用到,就會被垃圾回收。

          這幾個例子都是創(chuàng)建一個就刪除一個,那么如果先緩存下來,然后再刪除呢?

          看這個例子:

                 var i = 0;

                 while(i++<5000){

                       

                        (function(){

                               var o = document.createElement("<div>");

                               o.onclick = foo;

                               o.innerHTML = "AAA";

                              

                               buf.push(o);//如果后面不做處理,則不會被回收哦

                        })();

                 }

           

                 //直接清空buf

                 buf.length = 0;

                 /*

                 //嘗試逐個置空方法

                 for(var i=0;i<buf.length;i++){

                        //document.body.removeChild(buf[i]);

                        buf[i] = null;

                 }

                 */

                 //嘗試pop

                 //while(buf.pop());

          該例子先將5000個對象存在buf里,然后再嘗試各種方法去釋放他們,也就是斷開對他們的引用。 然而實驗結(jié)果是: 占用較大的內(nèi)存,而且每次調(diào)用這個函數(shù),PF使用率都是相同的! 這說明,內(nèi)存不會累計增加。 然而也不會立即的釋放。雖然PF使用率數(shù)值沒變,但是由于再次調(diào)用還是占用這些,說明之前占用的內(nèi)存被新的內(nèi)容覆蓋了。然而總體情況,還是沒有得到理想的釋放效果。 很讓人郁悶,為什么創(chuàng)建一個就釋放一個就可以,創(chuàng)建多個然后再釋放就不完全了呢?

          然而這種情況只是在測試,畢竟沒人會去創(chuàng)建一堆不掛載到頁面dom的元素吧~

           

          再來看掛載到dom樹上的情況:

          下面的例子也OK

          while(i++<5000){         

                        (function(){

                               var o = document.createElement("<div>");

                               o.onclick = foo; //因為定義在了外部,沒有循環(huán)引用問題

                               o.innerHTML = "AAA";

                               document.body.appendChild(o);

                               document.body.removeChild(o);

                        })();

          }

           

          如果去掉removeChild一行,內(nèi)存情況也尚好,只是增加一點,這是正常的,畢竟顯示到頁面上需要占用內(nèi)存。 然而讓我不解的是: 為什么append到頁面就占用很少內(nèi)存,而保存

          buf里面就會占用很大內(nèi)存呢??不解!
          而且,如果先append,然后保存到buf里,占用內(nèi)存依然很少!
          還有如果不設(shè)置innerHTML, 則占用內(nèi)存極少! 里面的原因我就猜不到了.

          我只能得出這樣的結(jié)論: IE中,創(chuàng)建一個元素,務(wù)必把它掛載到dom樹上! 這樣即使不remove,刷新后內(nèi)存也會釋放。

           

          如果將刪除代碼移到function外面, 情況一樣,沒有泄露。

           

          再來實驗將5000個對象掛載后,然后再刪除的情況

          我們通過buf保存對象的引用:

                 while(i++<10000){

                        var o;

                        (function(){

                               o = document.createElement("<div>");

                               o.onclick = foo;

                               o.innerHTML = "haha";

                               o.style.left="10px";

                               document.body.appendChild(o);

                               buf.push(o);//保存引用

                        })();

                 }

                 // 這里調(diào)用removeChild來釋放

                 while(buf.length>0){

                        document.body.removeChild(buf.pop());

                 }

          測試結(jié)果還不錯,雖然內(nèi)存會上升,但是多次調(diào)用會維持在一個水平上,這說明,每次調(diào)用時,新的對象占用舊的對象的內(nèi)存,因此不會累計增加,還算OK. 

          結(jié)論是:同未掛載的情況類似,如果一次性緩存多個對象然后統(tǒng)一removeChild來清除,則IE不會立即釋放內(nèi)存,如果有新的變量或者對象出現(xiàn),則會覆蓋那部分內(nèi)存。總的情況還不差。

           

          使用另一種方式刪除:定義一個看不見的元素gb當(dāng)做垃圾站

                 /*

                 while(buf.length>0){

                        //首先說明,一個元素只能掛載到一個點上,因為dom是一個樹結(jié)構(gòu),

                        //元素對象只是將指針掛載到樹的某個位置,之前對象在body,現(xiàn)在掛載到了

                        //gb,那么body中就不顯示了,而轉(zhuǎn)到gb上來,

                        //另外如果將一個元素innerHTML置空,意味著其子元素的內(nèi)存被釋放!

                        //所以,這是一個名副其實的回收站~哈哈

                        gb.appendChild(buf.pop());

                        gb.innerHTML = "";

                       

                 }

                 */

           

          這種情況得到的結(jié)果和上面類似,然而內(nèi)存占用更少,上面是35M左右,而這個維持在25M左右,情況好了不少。 只是效率低了一點,因為有append操作

          其實如果不做清除的時候,內(nèi)存占用到了25M. 而清除之后,第一種方法35M,第二種方法25M, 似乎根本沒有清除,其實還是這樣,新的內(nèi)容會覆蓋舊的內(nèi)存,只是任務(wù)管理器沒有顯示出來。因為如果不清除,多次調(diào)用后內(nèi)存是累計增加的,而清除后會維持在一個水平。

           

          還有pop的速度很慢,改成for會快很多!

          var l=buf.length;

                 for(var i=0;i<l;i++){

                        gb.appendChild(buf[i]);

                 }

                 gb.innerHTML = ""; //置空,子元素全被釋放!

                 buf.length = 0;        //重置buf

           

          所以,使用這種方法清除元素是再好不過的了。 可是需要注意的是,如果內(nèi)部元素有循環(huán)引用的現(xiàn)象,清除之前一定要先把循環(huán)引用斷開,方法就是遞歸的清除類型為function的屬性。

           

          只要有善于發(fā)現(xiàn)循環(huán)引用的良好習(xí)慣~ 問題就不是問題了~

          posted on 2009-02-12 04:40 backup2007 閱讀(1885) 評論(0)  編輯  收藏 所屬分類: 隨手寫寫


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 滨州市| 宁安市| 申扎县| 锡林郭勒盟| 疏附县| 龙州县| 尼勒克县| 柳州市| 怀柔区| 河曲县| 盐亭县| 新丰县| 轮台县| 舟曲县| 左贡县| 应城市| 荥经县| 霞浦县| 资溪县| 海城市| 健康| 张家川| 景泰县| 蒙山县| 南安市| 彩票| 乐昌市| 鄂尔多斯市| 蒙自县| 鄂托克旗| 长治市| 邵阳市| 禄劝| 平潭县| 镇康县| 宜良县| 黔东| 深泽县| 兰西县| 北宁市| 桦川县|