backup2007

          導(dǎo)航

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

          統(tǒng)計(jì)

          公告

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


          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

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

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

                                     

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

          測(cè)試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, 而是過后在另外一個(gè)函數(shù)中 gb.innerHTML=””

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

           

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

           

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

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

           

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

           

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

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

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

           

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

          得出:

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

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

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

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

          var o =document.createElement(“div”)

          o.onclick = foo;

          這樣就不會(huì)有泄露!

           

          規(guī)則2  不討論內(nèi)聯(lián)事件對(duì)象的情況,因?yàn)槟鞘莻€(gè)特例, 來看這樣的情況

          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)

                       

                        })();

                 }

          }

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

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

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

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

           

          3

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

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

          function test2(){

                 var i = 0;

                 while(i++<5000){

                        //(function(){

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

                               o.innerHTML = "AAA";

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

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

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

           

                        //})();

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

                 }

                

          }

           

           

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

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

          看這個(gè)例子:

                 var i = 0;

                 while(i++<5000){

                       

                        (function(){

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

                               o.onclick = foo;

                               o.innerHTML = "AAA";

                              

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

                        })();

                 }

           

                 //直接清空buf

                 buf.length = 0;

                 /*

                 //嘗試逐個(gè)置空方法

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

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

                        buf[i] = null;

                 }

                 */

                 //嘗試pop

                 //while(buf.pop());

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

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

           

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

          下面的例子也OK

          while(i++<5000){         

                        (function(){

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

                               o.onclick = foo; //因?yàn)槎x在了外部,沒有循環(huán)引用問題

                               o.innerHTML = "AAA";

                               document.body.appendChild(o);

                               document.body.removeChild(o);

                        })();

          }

           

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

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

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

           

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

           

          再來實(shí)驗(yàn)將5000個(gè)對(duì)象掛載后,然后再刪除的情況

          我們通過buf保存對(duì)象的引用:

                 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());

                 }

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

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

           

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

                 /*

                 while(buf.length>0){

                        //首先說明,一個(gè)元素只能掛載到一個(gè)點(diǎn)上,因?yàn)?/span>dom是一個(gè)樹結(jié)構(gòu),

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

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

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

                        //所以,這是一個(gè)名副其實(shí)的回收站~哈哈

                        gb.appendChild(buf.pop());

                        gb.innerHTML = "";

                       

                 }

                 */

           

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

          其實(shí)如果不做清除的時(shí)候,內(nèi)存占用到了25M. 而清除之后,第一種方法35M,第二種方法25M, 似乎根本沒有清除,其實(shí)還是這樣,新的內(nèi)容會(huì)覆蓋舊的內(nèi)存,只是任務(wù)管理器沒有顯示出來。因?yàn)槿绻磺宄啻握{(diào)用后內(nèi)存是累計(jì)增加的,而清除后會(huì)維持在一個(gè)水平。

           

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

          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) 評(píng)論(0)  編輯  收藏 所屬分類: 隨手寫寫


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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 郁南县| 翁牛特旗| 醴陵市| 成武县| 商丘市| 海盐县| 嘉荫县| 双流县| 迁西县| 田东县| 汾阳市| 栖霞市| 德格县| 龙海市| 株洲市| 延安市| 阜康市| 天峻县| 扎囊县| 盐源县| 保靖县| 阿尔山市| 武强县| 阜新| 大邑县| 泰州市| 新和县| 博白县| 平阳县| 米林县| 安龙县| 吴江市| 凤庆县| 临江市| 锦屏县| 永仁县| 伽师县| 翁牛特旗| 阳信县| 元谋县| 山丹县|