如何删除JavaScript画布呈现上下文的状态堆栈

How to delete the state stack for a JavaScript canvas rendering context?

本文关键字:上下文 布呈现 状态 堆栈 JavaScript 何删除 删除      更新时间:2023-09-26

我最近在JavaScript中使用<canvas>,发现了造成严重"内存泄漏"(更像内存爆炸)的可能性。使用画布上下文时,可以使用context.save()将绘图样式添加到"状态堆栈",使用context.restore()将其删除。(有关MDN上的渲染上下文,请参阅文档。)

当您碰巧连续保存到状态堆栈而不进行恢复时,就会出现问题。在Chrome v50和Firefox v45中,这似乎只占用了越来越多的私人内存,最终导致浏览器选项卡崩溃

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

[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