由于帧而导致的addEventListener内存泄漏 [英] addEventListener memory leak due to frames

查看:128
本文介绍了由于帧而导致的addEventListener内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a GreaseMonkey script that works on a site that uses frames as an integral part of its interface. This script leaks memory like a sieve, and I believe it is due to my use of addEventListener within one of the frames. Quite simply, I attach a variety of event listeners, then the frame reloads and I attach the event listeners, and then the frame reloads, around and around for hundreds or possibly thousands of iterations as you interact with various elements in this frame or others. By the end of it, Firefox has gone from ~300M of memory to as much as 2G (or crashes before it gets there).

我读到某个地方做了一个完整的页面重新加载将允许FireFox的垃圾收集例程启动并从孤立的事件处理程序中恢复所有内存,并且当我的脚本运行一段时间后,当我按F5时,在大约10秒内内存恢复到300M。不幸的是,这打破了网站中的一个不同的框架(一个非常流行的聊天窗口),所以虽然它确实似乎证实我怀疑addEventListener是责备,但它不是一个真正的解决方案选项。

I read somewhere that doing a full page reload would allow FireFox's garbage collection routines to kick in and recover all the memory from orphaned event handlers, and sure enough when I hit F5 after my script has run for a while, within about 10 seconds the memory is back down to 300M. Unfortunately this breaks a different frame in the site (a very popular chat window), so while it does seem to confirm my suspicion that addEventListener is to blame, it's not really an option as a solution.

在不强制整页刷新的情况下,我还能做些什么才能正常释放内存?

Is there something else I can do to free up the memory properly without forcing a full page refresh?

(目前使用GM 1.5和FF 17,但这个问题自GM 0.8 / FF 4以后就存在了。)

(Currently using GM 1.5 and FF 17, but the issue has existed since GM 0.8/FF 4 or thereabouts.)

推荐答案

没有看到完整的脚本,或者 简短,自包含,可编辑的例子 ,我们不能成为确定发生了什么。可能是 addEventListener 不是问题。

Without seeing your complete script, or a Short, Self Contained, Compilable Example, we can't be sure of what is going on. It may be that addEventListener is not the problem.

以下是更好代码的一些策略,内存泄漏更少:

Here are some strategies for better code, with fewer memory leaks:


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

  1. Inline/anonymous functions are often a culprit, especially with event handlers.

差/漏:

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*/
}

请注意,对于用户脚本,您应无论如何都要避免 onclick 等。

Note that for userscripts you should avoid onclick, etc. anyway.

同样不要使用JS HTML属性。 EG不使用< span onclick =callSomeFunction()> 等。

Likewise do not use JS on HTML attributes. EG don't use <span onclick="callSomeFunction()">, etc.

将iframe中运行的代码最小化为您明确需要的代码。

Minimize the code that runs in iframes to only that code you explicitly want.


  1. 使用 @include @exclude ,以及 @match 指令,以阻止尽可能多的不需要的iframe。

  2. 包装所有不需要在iframe中运行的代码阻止如下:

  1. Use the @include, @exclude, and @match directives to block as many unwanted iframes as possible.
  2. Wrap all code that doesn't need to run in iframes in a block like so:

if (window.top === window.self) {
  // Not in a frame
}



  • 不要使用 innerHTML

    对于许多元素或元素来说,使用AJAX,不要使用 addEventListener()或jQuery的 .bind()。 click()等。

    这可能会复制数千个节点的侦听器。

    For lots of elements, or elements that come and go with AJAX, do not use addEventListener() or jQuery's .bind(), .click(), etc.
    This replicates the listener across, potentially, thousands of nodes.

    使用 jQuery的 .on() 。这样,监听器只连接一次并通过冒泡适当地触发。 (请注意,在一些罕见的情况下, .on()可以被页面的javascript阻止。)

    Use jQuery's .on(). That way the listener is attached only once and triggers appropriately via bubbling. (Note that in some rare-ish cases .on() can be blocked by the page's javascript.)

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

    In your case, you probably want something like:

    $(document).on ("click", "YOUR ELEM SELECTOR", clickHandler);
    
    function clickHandler (evt) {
        /*do something*/
    }
    


  • 为避免出现意外循环引用或孤立项,请使用jQuery添加或删除元素,而不是直接使用DOM方法,如 createElement() appendChild()等。

    jQuery经过设计/测试,可以最大限度地减少这些事情。

  • To avoid surprise circular references or orphaned items, use jQuery to add or remove elements, rather than direct DOM methods like createElement(), appendChild(), etc.
    jQuery is designed/tested to minimize such things.

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

    Beware of overusing GM_setValue(). It easily can use lots of global resources or cause a script instance to crash.


    1. 对于同域值,请使用 localStorage

    2. 不要使用 GM_setValue()来存储除字符串之外的任何内容。对于其他任何事情,请使用序列化程序,例如 GM_SuperValue 。即使看起来无辜的整数也会导致默认的 GM_setValue()崩溃。

    3. 而不是存储大量小变量,可能会更好将它们包装在一个对象中并使用其中一个序列化程序存储

    1. For same-domain values, use localStorage.
    2. Do not use GM_setValue() to store anything but strings. For anything else, use a serializer such as GM_SuperValue. Even innocent looking integers can cause the default GM_setValue() to crash.
    3. Rather than store lots of small variables, it may be better to wrap them in an object and store that with one of the serializers.


    始终检查返回值并假设元素可能丢失:

    这是(唉,唉,典型):

    Always check return values and assume that elements can be missing:
    This is poor (and, alas, typical):

    $("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;
    

    见下文。

    注意递归/内联 setTimeout()调用。使用 setInterval()进行重复计时。就像使用事件处理程序一样,不要使用内联/匿名函数。

    Beware of recursive / inline setTimeout() calls. Use setInterval() for repeated timing. Just like with event handlers, do not use inline/anonymous functions.

    通过 JSLint 运行您的代码。

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

    Avoid eval() and auto/hidden eval() invocations.

    完成后,将变量设置为 null 请参阅此示例。

    Set variables to null when you are done with them. See this, for example.

    参考: 你知道什么可能导致JavaScript中的内存泄漏吗?

    有关JS内存泄漏的补充说明

    Mozilla 性能:泄漏工具

    这篇关于由于帧而导致的addEventListener内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆