javascript - 由于帧导致的 addEventListener 内存泄漏

标签 javascript memory greasemonkey addeventlistener

我有一个 GreaseMonkey 脚本,它在一个使用框架作为其界面不可或缺的部分的网站上运行。这个脚本像筛子一样泄漏内存,我相信这是由于我在其中一个框架中使用了 addEventListener。很简单,我附加各种事件监听器,然后重新加载框架,我附加事件监听器,然后框架重新加载,当您与此框架或其他框架中的各种元素交互时,反复进行数百次或可能数千次迭代。到最后,Firefox 的内存从大约 300M 增加到 2G(或者在到达之前就崩溃了)。

我在某处读到,重新加载整个页面将允许 FireFox 的垃圾收集例程启动并从孤立的事件处理程序中恢复所有内存,当我在脚本运行一段时间后按下 F5 时,果然在大约 10 秒内seconds 内存回落到 300M。不幸的是,这破坏了站点中的不同框架(一个非常流行的聊天窗口),因此虽然它似乎证实了我对 addEventListener 是罪魁祸首的怀疑,但它并不是真正的解决方案。

我还能做些什么来正确释放内存而不强制刷新整个页面吗?

(目前使用的是GM 1.5和FF 17,但是这个问题从GM 0.8/FF 4左右就已经存在了。)

最佳答案

没有看到您的完整脚本,或 Short, Self Contained, Compilable Example ,我们无法确定发生了什么。可能是addEventListener不是问题。

这里有一些更好的代码和更少的内存泄漏的策略:

  1. 内联/匿名函数通常是罪魁祸首,尤其是对于事件处理程序。

    差/漏水:

    elem.onclick = function () {/*do something*/};
    elem.addEventListener ("click", function() {/*do something*/}, false);
    $("elem").click ( function () {/*do something*/} );
    

    不会泄漏,也更容易维护:

    elem.onclick = clickHandler;
    elem.addEventListener ("click", clickHandler, false);
    $("elem").click (clickHandler);
    
    function clickHandler (evt) {
        /*do something*/
    }
    

    请注意,对于用户脚本,您应该 avoid onclick , etc. anyway.

  2. 同样不要在 HTML 属性上使用 JS。 EG 不要使用 <span onclick="callSomeFunction()">

  3. 尽量减少在 iframe 中运行的代码,只保留您明确需要的代码。

    1. 使用@include , @exclude , 和 @match尽可能多地阻止不需要的 iframe 的指令。
    2. Wrap all code that doesn't need to run in iframes in a block像这样:

      if (window.top === window.self) {
        // Not in a frame
      }
      
  4. 不要使用 innerHTML .

  5. 对于很多元素,或者与 AJAX 一起来来去去的元素,不要使用 addEventListener()或 jQuery 的 .bind() , .click()
    这可能会跨数千个节点复制监听器。

    使用jQuery's .on() .这样,监听器仅附加一次并通过冒泡适当触发。 (请注意,在一些罕见的情况下,.on() 可能会被页面的 javascript 阻止。)

    在你的情况下,你可能想要这样的东西:

    $(document).on ("click", "YOUR ELEM SELECTOR", clickHandler);
    
    function clickHandler (evt) {
        /*do something*/
    }
    
  6. 为避免意外的循环引用或孤立项,请使用 jQuery 添加或删除元素,而不是像 createElement() 这样的直接 DOM 方法。 , appendChild()
    jQuery 被设计/测试以最小化这些事情。

  7. 谨防过度使用 GM_setValue() .它很容易使用大量全局资源或导致脚本实例崩溃。

    1. 对于同域值,使用 localStorage .
    2. 不要使用 GM_setValue()存储除字符串以外的任何内容。对于其他任何事情,请使用序列化程序,例如 GM_SuperValue .即使看起来无辜的整数也会导致默认 GM_setValue()崩溃。
    3. 与其存储大量小变量,不如将它们包装在一个对象中并使用其中一个序列化器存储那个


  8. 始终检查返回值并假定可能缺少元素:
    这是(唉,很典型):

    $("selector").text($("selector").text().match(/foo=([bar]+)/)[1]);
    

    更好:

    var salesItemDiv    = $("selector");
    var fooMatch        = salesItemDiv.text ().match (/\bfoo\s*=\s*([bar]+)\b/i);
    if (fooMatch  &&  fooMatch.length > 1) {
        salesItemDiv.text ( fooMatch[1] );
    }
    

    后面可能是:

    salesItemDiv = fooMatch = null;
    

    见下文。

  9. 谨防递归/内联 setTimeout()电话。使用 setInterval()重复计时。就像事件处理程序一样,不要使用内联/匿名函数。

  10. 通过 JSLint 运行您的代码.

  11. 避免 eval()auto/hidden eval() invocations .

  12. 将变量设置为 null当你完成它们时。 See this, for example.

  13. 引用:"Do you know what may cause memory leaks in JavaScript?"

  14. Additional reading on JS memory leaks

  15. Mozilla Performance: Leak Tools

关于javascript - 由于帧导致的 addEventListener 内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13677589/

相关文章:

javascript - 求多边形的周长

javascript - SyntaxError : JSON. 解析:意外的数据结束

java - 如何减少 JVM 中的已提交堆内存

objective-c - 在 Objective-C 中删除动态创建的对象时,我应该将什么设置为 nil?

JavaScript/Greasemonkey : Avoiding FireFox Security Warning when Submitting a Form from a Secure Page

javascript - 如何使用带有 greasemonkey 的 google maps api 读取地址表并跟踪路线?

javascript - Node.js 可以编辑视频文件吗?

javascript - 从数组中提取的列表中缺少值

c - 如果我尝试在 C 中定义一个指向字符串文字的指针,会发生什么情况?

javascript - 隐藏名称中带有空格的类