¿Cómo eliminar la stack de estado para un contexto de representación de canvas de JavaScript?

Recientemente estuve trabajando con en JavaScript y descubrí la posibilidad de crear una “pérdida de memoria” realmente mala (más como una explosión de memoria). Al trabajar con el contexto del canvas, tiene la capacidad de hacer context.save() para agregar los estilos de dibujo a la “stack de estado” y context.restre() para eliminarlo. (Consulte la documentación para el contexto de representación en MDN ).

El problema se produce cuando se guarda continuamente en la stack de estado sin restaurar. En Chrome v50 y Firefox v45, esto parece ocupar más y más memoria privada, y eventualmente se bloquea la pestaña del navegador. (Por cierto, la memoria de JavaScript no se ve afectada en Chrome, por lo que es muy difícil depurar esto con las herramientas de perfilador / línea de tiempo).

Mi pregunta: ¿Cómo puede borrar o eliminar la stack de estados para un contexto de canvas? Con una matriz normal, podría verificar la length , recortarla con splice o simplemente restablecer para volver a vaciar [] , pero no he visto una forma de hacer nada de esto con la stack de estados.

[I] … descubrí la posibilidad de crear una “pérdida de memoria” realmente mala

Esto no es técnicamente una pérdida de memoria. Una fuga sería asignar memoria y perder el puntero para que no se pueda liberar. En este caso, el puntero se rastrea, pero la memoria no se libera.

El problema se produce cuando se guarda continuamente en la stack de estado sin restaurar.

Eso es lo que se espera. La asignación de memoria sin liberarla acumulará bloques de memoria asignados.

¿Cómo se puede borrar o eliminar la stack de estados para un contexto de canvas?

La única forma es restaurar todos los estados guardados o restablecer el contexto estableciendo un cierto tamaño en el elemento del canvas (es decir, canvas.width = canvas.width ).

También es seguro llamar a restre() más veces que save() (en cuyo caso simplemente regresa sin hacer nada), por lo que en teoría podría ejecutarlo a través de un bucle de n número de iteraciones. Sin embargo, este último sería más en la categoría de mala práctica.

Pero con eso se dice: si hay una discrepancia en el número de operaciones de guardar y restaurar cuando se supone que es igual, generalmente indica un problema en otra parte del código. Resolver el problema con un restablecimiento o ejecutar varias restauraciones en la publicación probablemente solo contribuirá a encubrir el problema real.

Aquí hay un ejemplo de cómo realizar un seguimiento del recuento de llamadas guardar / restaurar:

 // NOTE: this code needs to run before a canvas context is created CanvasRenderingContext2D.prototype.__save = CanvasRenderingContext2D.prototype.save; CanvasRenderingContext2D.prototype.__restre = CanvasRenderingContext2D.prototype.restre; // Our patch vectors CanvasRenderingContext2D.prototype.__tracker = 0; CanvasRenderingContext2D.prototype.save = function() { this.__tracker++; console.log("Track save:", this.__tracker); this.__save() } CanvasRenderingContext2D.prototype.restre = function() { this.__tracker--; console.log("Track restre:", this.__tracker); this.__restre() } // 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.restre(); // single restre() ctx.trackstat(); // should report mismatch of 1 ctx.restre(); // last restre() ctx.trackstat(); // should report OK