我有一个 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
不是问题。
这里有一些更好的代码和更少的内存泄漏的策略:
内联/匿名函数通常是罪魁祸首,尤其是对于事件处理程序。
差/漏水:
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.同样不要在 HTML 属性上使用 JS。 EG 不要使用
<span onclick="callSomeFunction()">
等尽量减少在 iframe 中运行的代码,只保留您明确需要的代码。
- 使用
@include
,@exclude
, 和@match
尽可能多地阻止不需要的 iframe 的指令。 Wrap all code that doesn't need to run in iframes in a block像这样:
if (window.top === window.self) { // Not in a frame }
- 使用
不要使用
innerHTML
.对于很多元素,或者与 AJAX 一起来来去去的元素,不要使用
addEventListener()
或 jQuery 的.bind()
,.click()
等
这可能会跨数千个节点复制监听器。使用jQuery's
.on()
.这样,监听器仅附加一次并通过冒泡适当触发。 (请注意,在一些罕见的情况下,.on()
可能会被页面的 javascript 阻止。)在你的情况下,你可能想要这样的东西:
$(document).on ("click", "YOUR ELEM SELECTOR", clickHandler); function clickHandler (evt) { /*do something*/ }
为避免意外的循环引用或孤立项,请使用 jQuery 添加或删除元素,而不是像
createElement()
这样的直接 DOM 方法。 ,appendChild()
等
jQuery 被设计/测试以最小化这些事情。谨防过度使用
GM_setValue()
.它很容易使用大量全局资源或导致脚本实例崩溃。- 对于同域值,使用
localStorage
. - 不要使用
GM_setValue()
存储除字符串以外的任何内容。对于其他任何事情,请使用序列化程序,例如GM_SuperValue
.即使看起来无辜的整数也会导致默认GM_setValue()
崩溃。 - 与其存储大量小变量,不如将它们包装在一个对象中并使用其中一个序列化器存储那个。
- 对于同域值,使用
始终检查返回值并假定可能缺少元素:
这是差(唉,很典型):$("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()
重复计时。就像事件处理程序一样,不要使用内联/匿名函数。通过 JSLint 运行您的代码.
避免
eval()
和 auto/hiddeneval()
invocations .将变量设置为
null
当你完成它们时。 See this, for example.
关于javascript - 由于帧导致的 addEventListener 内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13677589/