在img.onload上留下处理程序是内存泄漏?

gma*_*man 5 html javascript memory-leaks

这段代码在浏览器中导致内存泄漏是否正确?

/**
 * @param {Canvas2DRenderingContext} ctx
 * @param {string} url
 */
function loadImageDrawIntoCanvas(ctx, x, y, url) {
  var img = new Image();
  img.onload = function() {
    ctx.drawImage(img, x, y);
  }
  img.src = url;
};
Run Code Online (Sandbox Code Playgroud)

我的理解是因为img是一个DOM元素,因为我用img.onload浏览器将JavaScript附加到它上面,所以永远不会垃圾收集它.要纠正我需要清除img.onload的内容

/**
 * @param {Canvas2DRenderingContext} ctx
 * @param {string} url
 */
function loadImageDrawIntoCanvas(ctx, x, y, url) {
  var img = new Image();
  img.onload = function() {
    ctx.drawImage(img, x, y);
    img.onload = null;          // detach the javascript from the Image
    img = null;                 // needed also so the closure doesn't keep
                                // a reference to the Image?
  }
  img.src = url;
};
Run Code Online (Sandbox Code Playgroud)

Zac*_*chB 3

只要浏览器正确实现,它就不应该是泄漏。

旧版本的 Internet Explorer(7 及更早版本)的 GC 无法处理 JS 和 DOM 节点之间的循环引用。因此,有很多指南建议在删除 DOM 节点之前清除事件侦听器,并且 jQuery 会自动执行此操作。(注意:其他浏览器可能在某些时候出现过 GC 错误,但旧版 IE 是最有名的。)

这里有趣的部分是 GC 需要知道onload将来是否会再次被触发。

我刚刚尝试使用与您发布的代码类似的代码将 275 MB 的图像渲染到画布上,并且 Chrome 不会泄漏。(相比之下,如果我将图像存储在循环之外的数组中,则保留 275 MB。)Firefox 可能会泄漏[一些?],但这很难说,因为它的内存开销比 Chrome 高得多。

为什么?

  • 在 javascript 端,onload既loadImageDrawIntoCanvas完成执行,又没有剩余的引用img。
  • img在浏览器实现方面,他们做了一些功能上相当于在调用时增加引用计数,并在触发img.src=后减少引用计数。Chrome针对onload此类泄漏有两项测试 ( 1、2 )。