【问题标题】:How to delete the state stack for a JavaScript canvas rendering context?如何删除 JavaScript 画布渲染上下文的状态堆栈?
【发布时间】:2016-08-26 03:25:04
【问题描述】:

我最近在 JavaScript 中使用 <canvas>,并发现可能会造成非常糟糕的“内存泄漏”(更像是内存爆炸)。使用画布上下文时,您可以使用context.save() 将绘图样式添加到“状态堆栈”,并使用context.restore() 将其删除。 (见the documentation for the rendering context on MDN。)

当您碰巧连续保存到状态堆栈而不恢复时,就会出现问题。在 Chrome v50 和 Firefox v45 中,这似乎只是占用了越来越多的私有内存,最终导致浏览器选项卡崩溃。 (顺便说一下,JavaScript 内存在 Chrome 中不受影响,因此很难使用分析器/时间线工具进行调试。)

我的问题:如何清除或删除画布上下文的状态堆栈? 使用普通数组,您可以检查length,用@987654326 修剪它@ 或简单地重置为空[],但我还没有看到用状态堆栈执行任何此操作的方法。

【问题讨论】:

  • 我认为你不能真正拥有堆栈的“长度”。为避免这种情况,只需记住每次您不再需要保存状态时,您必须 restore 它。 (还要注意,它会保存你上下文的全部属性,当大多数时候你只需要保存一两个,我个人尽量避免调用这个重方法)。所以你的问题的答案已经在你的问题中了。
  • 我正在寻找一个解决方案,如果“简单地记住”失败了,它可能有助于调试。想象一下,您刚刚获得了一个上下文……您将如何清理它或检测是否存在持续存在的问题?
  • 如果有人能解释为什么私有内存增加而不是 Chrome 中的 JavaScript 内存增加。这使得诊断变得非常困难。

标签: javascript canvas memory-management memory-leaks html5-canvas


【解决方案1】:

[I].. 发现了造成非常糟糕的“内存泄漏”的可能性

这在技术上不是内存泄漏。泄漏将是分配内存并释放指向它的指针,因此无法释放它。在这种情况下,指针被跟踪但内存没有被释放。

当您碰巧连续保存到状态堆栈而不恢复时会出现问题。

这是意料之中的。分配内存而不释放它会累积分配的内存块。

如何清除或删除画布上下文的状态堆栈?

唯一的方法是要么恢复所有保存的状态,要么通过为画布元素设置一些大小来重置上下文(即canvas.width = canvas.width)。

调用restore() 的次数也比调用save() 的次数多(在这种情况下,它只是返回而不做任何事情),所以理论上你可以通过n 迭代次数的循环运行它。不过,后者更属于不良做法。

但话虽如此:如果保存和恢复的数量在假设相等时不匹配,通常表明代码中的其他地方存在问题。通过重置或在后期运行多次恢复来解决问题可能只会掩盖实际问题。

这是一个关于如何跟踪保存/恢复调用计数的示例 -

// NOTE: this code needs to run before a canvas context is created
CanvasRenderingContext2D.prototype.__save = CanvasRenderingContext2D.prototype.save;
CanvasRenderingContext2D.prototype.__restore = CanvasRenderingContext2D.prototype.restore;

// Our patch vectors
CanvasRenderingContext2D.prototype.__tracker = 0;
CanvasRenderingContext2D.prototype.save = function() {
  this.__tracker++;
  console.log("Track save:", this.__tracker);
  this.__save() 
}

CanvasRenderingContext2D.prototype.restore = function() {
  this.__tracker--;
  console.log("Track restore:", this.__tracker);
  this.__restore() 
}

// custom method to dump status
CanvasRenderingContext2D.prototype.trackstat = function() {
  if (this.__tracker)
    console.warn("Track stat:", this.__tracker);
  else
    console.log("Track stat: OK");
}

var ctx = document.createElement("canvas").getContext("2d");
ctx.save();                     // do a couple of save()s
ctx.save();
ctx.restore();                  // single restore()
ctx.trackstat();                // should report mismatch of 1
ctx.restore();                  // last restore()
ctx.trackstat();                // should report OK

【讨论】:

  • “我们需要确保我们自己跟踪推送(或保存)和弹出(恢复)的数量”
  • @Luke 访问确实是有限的。您可以“修补”调用以保持临时跟踪以进行调试。我将添加一个有关如何执行此操作的示例,但这当然就像为脚趾疼痛注射流感疫苗一样。问题表明设计存在问题。
  • 不,这确实是内存泄漏。甚至在技术上!
  • @Ryan 不,泄漏是“丢失”的内存。 持有您可以随时释放的内存只是不好的做法,而不是泄漏(除非有故意的尝试或浏览器,否则很难在诸如 JS 之类的语言中产生实际的内存泄漏涉及的错误)。
  • @K3N:泄漏是在不再使用时未释放的内存。这完全符合要求。认为在 JS 中很难造成内存泄漏的观点是……错误的。
猜你喜欢
  • 1970-01-01
  • 2019-05-07
  • 2015-02-07
  • 1970-01-01
  • 2012-06-18
  • 1970-01-01
  • 2019-10-30
  • 2011-07-28
相关资源
最近更新 更多