Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 408|回复: 0

jQuery1.72 内存泄露追踪(附解决方案)

[复制链接]
  • TA的每日心情
    奋斗
    昨天 16:00
  • 签到天数: 755 天

    [LV.10]以坛为家III

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    707348
    发表于 2021-4-20 17:18:58 | 显示全部楼层 |阅读模式

    场景

    异步回调,解析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 ,问题解决。 

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-6-27 17:20 , Processed in 0.070053 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表