5

每隔一段时间,我就会发现自己在按照以下方式做一些事情

var img = new Image();
img.src = 'php/getpic.php?z=' + imid + '&th=0';
img.onload = function(){drawImages(img,contexts,sizes)};

解释

  1. 创建一个 HTML 图像元素。
  2. 分配它的 src 属性
  3. 分配其 onload 事件
  4. 将一个或多个 Canvas 上下文传递给事件处理程序
  5. 将加载的图像绘制到画布上

我不清楚的是 - JavaScript 垃圾收集器会处理丢弃 img 元素的任务,还是我需要自己做,否则会面临缓慢的内存泄漏?

4

2 回答 2

7

它会在IE 6 和 7 (以及非常旧的 FF 版本)中泄漏,因为它会在 JavaScript 和 DOM 之间创建循环引用。IE 6 和 7 不能垃圾收集在两个世界之间具有循环引用的任何对象,因为它们使用单​​独的垃圾收集器。

现代浏览器可以在不泄漏的情况下处理这个问题。

为防止它在 IE 6 和 7 中泄漏,请在完成以下操作后执行此操作img

img.onload = null;

如果您只关心现代浏览器,则不必担心。(我很高兴 IE 6 和 7 的市场份额终于低到足以表明这一点!)


更新

您分配给的函数onload创建一个闭包。该闭包包含对img. img只要该闭包存在于 JScript 的内存中(JScript 是 JavaScript 的 IE 实现的名称),就不能从 DOM 的内存中进行垃圾回收。同样,只要img存在于 DOM 的内存中,闭包就不能从 JScript 的内存中进行垃圾回收,因为img.onload它引用了您的函数。这是一个循环引用。换句话说,仅仅因为drawImages执行一次并不意味着它不会再次执行(JScript 引擎不知道onload只触发一次——那是 DOM 的域),所以 JScript 必须保持闭包处于活动状态。

您展示的模式是已知会在 IE 6 和 7 中造成内存泄漏的经典模式。它包括 (1) 一个 DOM 节点,(2) 该 DOM 节点上创建闭包的事件处理程序,以及 (3 ) 对闭包内该 DOM 节点的引用。

于 2012-11-20T15:32:24.277 回答
6

如果您担心内存泄漏,我建议您使用Google Chrome 的 Heap Debugger。在您的情况下,我做了一个简单的测试,可以验证您的特定模式是否会造成内存泄漏。

<html>
    <head>
        <script>
            function test() {
                var img = new Image();
                img.src = 'php/getpic.php?z=1&th=0';
                img.onload = function(){ drawImages(img,contexts,sizes); };
            }
        </script>
    </head>
    <body>
        <input type="button" onclick="test()" value="test" />
    </body>
</html>

我建议您在测试前按一次“测试”以避免结果中出现不必要的噪音(代码第一次运行时内存中会添加一些东西,这并不意味着存在内存泄漏)。

拍摄堆快照,按测试并拍摄堆快照。如果您想要实际结果,您必须将视图从 "Summary" 切换到"Comparaison"。如果视图是空的(在我测试的情况下),这意味着在该步骤中没有添加任何内容,这意味着它没有内存泄漏。在另一种情况下,它将向您显示快照之间添加或删除的内容。

注意:对于有自身问题的旧浏览器,此方法无法帮助您解决内存泄漏问题,但这是在大多数现代浏览器中找到会导致内存泄漏的事情的好步骤。

于 2012-11-20T16:06:54.010 回答