Javascript getImageData内存泄漏?
背景:在过去的一周里,我一直在开发一款基本上是多向的游戏,使用画布和JavaScript。我选择不清除画布上的每一帧,这样我的小线段就会留下一条轨迹。对于碰撞检测,我使用以下功能:Javascript getImageData内存泄漏?,javascript,canvas,memory-leaks,getimagedata,Javascript,Canvas,Memory Leaks,Getimagedata,背景:在过去的一周里,我一直在开发一款基本上是多向的游戏,使用画布和JavaScript。我选择不清除画布上的每一帧,这样我的小线段就会留下一条轨迹。对于碰撞检测,我使用以下功能: // 8 sensors for collision testing, positioned evenly around the brush point var detectionRadius = this.width / 2 + 1; //points just outside the circumference
// 8 sensors for collision testing, positioned evenly around the brush point
var detectionRadius = this.width / 2 + 1; //points just outside the circumference
var counter = 0;
var pixelData;
for (var i = 0; i < 16; i += 2) {
//collisionPixels[] is an array of 8 (x, y) offsets, spaced evenly around the center of the circle
var x = this.x + collisionPixels[i] * detectionRadius;
var y = this.y + collisionPixels[i + 1] * detectionRadius;
pixelData = context.getImageData(x,y,1,1).data; //pixel data at each point
if (pixelData[3] != 0) {
counter++;
}
}
if (counter > 4) {
this.collision();
}
编辑2:所以我尝试了Patrick的UInt8ClampedArray创意:
//8 sensors for collision testing, positioned evenly around the brush point
var detectionRadius = this.width / 2 + 1;
var counter = 0;
for (var i = 0; i < 16; i += 2) {
var x = this.x + collisionPixels[i] * detectionRadius;
var y = this.y + collisionPixels[i + 1] * detectionRadius;
//translate into UInt8ClampedArray for data
var index = (y * canvas.width + x) * 4 + 3; //+3 so we're at the alpha index
if (canvasArray[index] != 0) {
counter++;
}
}
希望我做得对。这是可行的,但是每玩一轮,内存和帧率都会变得更差。将上载一些堆快照
编辑3:
快照1:
快照2:
快照1位于第一局之后,快照2位于第二局之后
编辑4:尝试限制帧速率:
function loop() {
requestAnimationFrame(loop);
now = Date.now();
delta = now - lastUpdate;
//lastUpdate = now;
if (delta > interval) {
lastUpdate = now;
if (!paused) {
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
players[i].update(delta);
players[i].render();
}
}
}
}
}
它延迟了最终的性能影响,但使用此选项时内存仍在增加
编辑5:虽然我确信一定有更好的方法,但我找到了一个相当有效的解决方案。将帧速率限制在30左右实际上在长期性能方面是有效的,但我讨厌游戏以每秒30帧的速度运行。。所以我构建了一个循环,除了冲突处理(我以30 FPS的速度更新)之外,所有更新和渲染都有一个无上限的帧速率
function loop() {
requestAnimationFrame(loop);
now = Date.now();
delta = now - lastUpdate;
lastUpdate = now;
if (!paused) {
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
players[i].update(delta);
players[i].render();
}
}
if (now - lastCollisionUpdate > collisionInterval) {
canvasData = context.getImageData(0, 0, context.canvas.width, context.canvas.height).data;
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
if (players[i].detectCollisions()) {
players[i].collision();
}
}
}
lastCollisionUpdate = now;
}
canvasData = null;
}
}
//separate update cycle for collision detection
var collisionFPS = 30;
var lastCollisionUpdate;
var collisionInterval = 1000 / collisionFPS;
var canvasData;
function loop() {
requestAnimationFrame(loop);
now = Date.now();
delta = now - lastUpdate;
lastUpdate = now;
if (!paused) {
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
players[i].update(delta);
players[i].render();
}
}
if (now - lastCollisionUpdate > collisionInterval) {
canvasData = context.getImageData(0, 0, context.canvas.width, context.canvas.height).data;
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
if (players[i].detectCollisions()) {
players[i].collision();
}
}
}
lastCollisionUpdate = now;
}
canvasData = null;
}
}
函数循环(){
请求动画帧(循环);
now=Date.now();
delta=now-lastUpdate;
lastUpdate=now;
如果(!暂停){
对于(变量i=0;icollisionInterval){
canvasData=context.getImageData(0,0,context.canvas.width,context.canvas.height);
对于(变量i=0;i
谢谢你的回答。。你的很多想法都融入到了最终的产品中,我对此表示感谢。关闭此线程。是否存在可以调用
context.getImageData(0,0,context.canvas.width,context.canvas.height).data
的点,以便您可以使用单个uint8clampedaray
而不是使用多个?另外,当您处理完图像数据(即图像数据中的ImageData
,而不是TypedArray
)后,您可以尝试调用图像数据中的delete
,尽管我不确定这是否会释放内存。虽然我肯定肯定有更好的方法,但我找到了一个相当好的解决方案。将帧速率限制在30左右实际上在长期性能方面是有效的,但我讨厌游戏以每秒30帧的速度运行。。所以我构建了一个循环,除了冲突处理(我以30 FPS的速度更新)之外,所有更新和渲染都有一个无上限的帧速率
function loop() {
requestAnimationFrame(loop);
now = Date.now();
delta = now - lastUpdate;
lastUpdate = now;
if (!paused) {
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
players[i].update(delta);
players[i].render();
}
}
if (now - lastCollisionUpdate > collisionInterval) {
canvasData = context.getImageData(0, 0, context.canvas.width, context.canvas.height).data;
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
if (players[i].detectCollisions()) {
players[i].collision();
}
}
}
lastCollisionUpdate = now;
}
canvasData = null;
}
}
//separate update cycle for collision detection
var collisionFPS = 30;
var lastCollisionUpdate;
var collisionInterval = 1000 / collisionFPS;
var canvasData;
function loop() {
requestAnimationFrame(loop);
now = Date.now();
delta = now - lastUpdate;
lastUpdate = now;
if (!paused) {
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
players[i].update(delta);
players[i].render();
}
}
if (now - lastCollisionUpdate > collisionInterval) {
canvasData = context.getImageData(0, 0, context.canvas.width, context.canvas.height).data;
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
if (players[i].detectCollisions()) {
players[i].collision();
}
}
}
lastCollisionUpdate = now;
}
canvasData = null;
}
}
//冲突检测的单独更新周期
var碰撞fps=30;
var lastCollisionUpdate;
var碰撞间隔=1000/碰撞每秒;
var数据;
函数循环(){
请求动画帧(循环);
now=Date.now();
delta=now-lastUpdate;
lastUpdate=now;
如果(!暂停){
对于(变量i=0;icollisionInterval){
canvasData=context.getImageData(0,0,context.canvas.width,context.canvas.height);
对于(变量i=0;i
可能不是最好的解决方案,但它是一致的。什么是“skyrockets”,或多或少?你能提供一个可以运行的例子来说明这个问题吗?我在这段代码中没有看到内存泄漏的任何原因,但是代码效率不高,所以我怀疑问题更多的是你的游戏在时间上非常紧张,没有给GC留下多少时间。您能否向我们展示如何调用循环(例如,您是否使用setTimeout/Interval、requestAnimationFrame…)@KenFyrstenberg编辑到描述中;它是requestAnimationFrame()。今晚晚些时候我将尝试使用帧率上限,看看这是否给GC时间。这本身并不能解决您的问题,但您正在不必要地重新说明一些变量。我会在循环外定义x、y和索引。我尝试运行delete,但它不会释放内存。但是第一个想法听起来很有希望:对UInt8ClampedArray不太熟悉,所以我会给你回复。@JackBritton你的
pixelData
是一个UInt8ClampedArray
,只是为了澄清一下。
//separate update cycle for collision detection
var collisionFPS = 30;
var lastCollisionUpdate;
var collisionInterval = 1000 / collisionFPS;
var canvasData;
function loop() {
requestAnimationFrame(loop);
now = Date.now();
delta = now - lastUpdate;
lastUpdate = now;
if (!paused) {
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
players[i].update(delta);
players[i].render();
}
}
if (now - lastCollisionUpdate > collisionInterval) {
canvasData = context.getImageData(0, 0, context.canvas.width, context.canvas.height).data;
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
if (players[i].detectCollisions()) {
players[i].collision();
}
}
}
lastCollisionUpdate = now;
}
canvasData = null;
}
}