• 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 ,问题解决。 

    alarm   作者:NewSea     出处:http://newsea.cnblogs.com/    QQ,MSN:iamnewsea@hotmail.com

      如无特别标记说明,均为NewSea原创,版权私有,翻载必纠。欢迎交流,转载,但要在页面明显位置给出原文连接。谢谢。
  • 相关阅读:
    SHAP值学习笔记
    撸了一个 Feign 增强包 V2.0 升级版
    Spring Cloud Function SpEL表达式RCE漏洞复现分析
    从Docker挂载逃逸原理复现分析到BlueMoon实战
    CDN绕过技术总汇
    从防御者视角来看APT攻击
    psexec.py规避杀软
    通过Kuberneters Goat学习K8S安全(下)
    .NET混合开发解决方案6 检测是否已安装合适的WebView2运行时
    .NET混合开发解决方案9 WebView2控件的导航事件
  • 原文地址:https://www.cnblogs.com/newsea/p/2561201.html
Copyright © 2020-2023  润新知