jQuery1.72 內存泄露追蹤(附解決方案)


場景

異步回調,解析HTML,過濾出某一部分,加載到頁面。

我的代碼

   html = $(html) ;

追蹤

經查: jQuery在解析Html時,會有內存泄露。追蹤的執行代碼如下:

1. init:

  if (typeof selector === "string")

    ...

    ret = jQuery.buildFragment([match[1]], [doc]);

2. buildFragment

  jQuery.clean(args, doc, fragment, scripts);

3.clean 這才是核心,還不明白為什么起這個名字。 分析文章: http://www.cnblogs.com/nuysoft/archive/2012/01/11/2318651.html

  div = context.createElement("div"),      // 重點關注對象 .

  safeChildNodes = safeFragment.childNodes,  //safeFragment 是在加載完jQuery之后執行的。 它只是進行了創建,並沒有追加到 DOM 中。

  ...

  // Append wrapper element to unknown element safe doc fragment
  if (context === document) {
  // Use the fragment we've already created for this document
  safeFragment.appendChild(div);        //把div添加到 safeFragment,作用何在? 
  } else {
  // Use a fragment created with the owner document
  createSafeFragment(context).appendChild(div);
  }

  ...

  div.innerHTML = wrap[1] + elem + wrap[2];  //內存開始增長。之后並沒有回落,懷疑是清理有問題。

  ...

  div.parentNode.removeChild(div);    //很明顯,作者想到了要清理,但根據監測結果,內存並沒有回落。在HTML內容很大的情況下,觀察效果明顯。

 

解決方案

  作者的方案是,創建div,把它添加到 Fragment,再設置 innerHTML,和網上流傳的

url: http://archive.cnblogs.com/a/2260680/

出現這種內存泄漏需要有三個條件:   1. 內存中存在一個未加入DOM樹的元素   2. 給這個元素設置innerHTML,注意,必須是能創建元素並且綁定了DOM 0級事件   3. 必須在這個元素加入DOM樹前設置它的innerHTML

思想如出一轍,但有不同。

把我的代碼按網上方案進行改造。

 

var div = document.createElement("div");
document.body.appendChild(div) ;
div.innerHTML = html ;

html = $(div.childNodes );

...

 

document.body.removeChild(div);

 

使用上述方法后,發現IE下設置 innerHTML 后 childNodes 是一坨 html 代碼,並沒有解析成對象。調試jQuery代碼發現,jQuery解析時,執行了如下代碼:

1. 在 init 里: 

  match = quickExpr.exec(selector);

2. 在 clean 函數 時:

  elem = elem.replace(rxhtmlTag, "<$1></$2>");

而且,它在設置 innerHtml 時,進行發如下操作:

  div.innerHTML = wrap[1] + elem + wrap[2];

綜合jQuery寫法,修改如下:

html = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/.exec(html) ;
html = html[1].replace(/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, "<$1></$2>") ;

var div = document.createElement("div");
document.body.appendChild(div) ;
div.innerHTML = "div<div>" + html +"</div>";

html = $(div.lastChild.childNodes);

...

 document.body.removeChild(div);

 

使用 Drip,進行測試,使用 Show Memory Usage 查看,內存增加非常小,使用 Show Dom Usage 查看,曲線還是很明顯(但點Show Dom Leaks,頁面會變白)

使用 sIEve 進行測試,使用 Show Memory Usage 查看,內存增長曲線非常小。使用 Show Dom Usage 查看,曲線基本持平。

使用 任務管理器進行查看,發現內存可以回落。 sIEve 比 Drip 要准確。

下圖是 sIEve 的兩個曲線圖:

 

效果還是不錯的。

 

后記:

jQuery1.7.2 在 IE8 下與 frameset 有嚴重的內存泄露。

這只是在單頁面里進行測試,沒有問題。在 frameset 里,測試,刷新一下子頁面,或切換左邊的菜單,都會引起內存泄露。

環境 jQuery 1.7.2, IE8 , 使用 frameset。 

現象是刷新子頁面所有元素都泄露。包括 最外層的 frameset。

經排查, 改為 jQuery 1.7.1 ,問題解決。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM