javascript - getImageData - Web worker - 如何减少垃圾回收?

标签 javascript canvas garbage-collection

我有一个正在运行的网络工作人员 Canvas 更新脚本示例,但我注意到它每隔几秒就会停顿大约 200 毫秒。通常的循环时间约为 15 毫秒。

我猜这是垃圾回收——从分析器看来是这样。

http://codepen.io/SarahC/pen/bgBoMM

我认为是在这个函数中:

function nextFrame(){
  timeChart.start();
  workersWorking = workerCount;
  var stripHeight = ~~( h / workerCount );
  for(var i = 0; i < workerCount; i++){
    var localImageData = ctx.getImageData(0, stripHeight * i, w, stripHeight); /// This needs putting in constant memory.... GC takes ages here.
    workers[i].postMessage({imageData: localImageData, YPosition: stripHeight * i, threadNumber: i});
  }
}

如果是这个位造成了所有的垃圾内存,我不知道我能做些什么来为这些数据 block 继续使用相同的内存区域。

最佳答案

传递 imageData 的缓冲区而不是 imageData 本身。

这样,你的缓冲区就是transferred (使用零拷贝操作),并且不会再污染主线程的内存。
否则,当您不传输它时,您的对象将被结构化克隆(就像您执行 JSON.parse(JSON.stringify(yourObject)); 一样),这意味着当您从 worker 发送回主线程时,您的计算机在内存中保存了相同数据的三个副本。

请注意,当传递给 worker 时,imageData 的数据在主线程中不再可用(如果您尝试 putImageData() 它会抛出错误)。 不幸的是,我不知道更改 ImageData 缓冲区的好方法,但由于 ImageData(),您可以在创建时设置缓冲区构造函数(显然在 IE 中仍然不受支持......),它确实只会创建一个指向 arrayBuffer 的指针。

所以当所有这些都被支持时,只有 ImageData 结构(基本上是一个对象 {width:XXX, height:XXX})被创建而不是一个沉重的缓冲区。其他一切都只是移动,不会污染内存。

let workerURL = URL.createObjectURL(new Blob([workerScript.textContent], {
  type: 'application/javascript'
}));

const worker = new Worker(workerURL);
worker.onmessage = e => {
  let buf = e.data,
    arr = new Uint8ClampedArray(buf),
    processedImageData;
  try {
    processedImageData = new ImageData(arr, imageData.width, imageData.height);
  } catch (e) {
    processedImageData = ctx.createImageData(imageData.width, imageData.height);
    processedImageData.data.set(arr);
  }
  // checks that we didn't created an useless buffer in this last step
  // IE will because it doesn't support new ImageData(buf)
  console.log('Does our TypedArray share the same buffer as the one we received ? ',
              arr.buffer === buf);
  console.log('Does our new imageData share the same buffer as the one we received ? ',
              processedImageData.data.buffer === buf);
  // Note that here a check for the original imageData's buffer has no sense
  //       since it has been emptied
  ctx.putImageData(processedImageData, 0, 0);
}

const ctx = canvas.getContext('2d');
ctx.fillStyle = 'green';
ctx.fillRect(20, 20, 60, 80);
let imageData = ctx.getImageData(0, 0, 300, 150);
// pass it as transferable
worker.postMessage(imageData.data.buffer, [imageData.data.buffer]);
console.log(imageData.data.length, 'now empty')
<script type="worker-script" id="workerScript">
	self.onmessage = e => {
		let buf = e.data,
		arr = new Uint8Array(buf);
		console.log('worker received', buf);
		for(let i =0; i<arr.length; i+=4){
			arr[i] = (arr[i] + 128) % 255;
			arr[i+1] = (arr[i+1] + 128) % 255;
			arr[i+2] = (arr[i+2] + 128) % 255;			
			}
		self.postMessage(buf, [buf]);
		// this won't print in stacksnippet's console
		// you have to check your dev tools' one
		console.log('worker now holds', buf.byteLength, 'empty');

		};
</script>
<canvas id="canvas"></canvas>

还有一个使用结构克隆的反例:

let workerURL = URL.createObjectURL(new Blob([workerScript.textContent], {
  type: 'application/javascript'
}));

const worker = new Worker(workerURL);
worker.onmessage = e => {
  let buf = e.data;
  // so our original imageData's arrayBuffer is still available
  imageData.data.set(buf);
  // Here we can check for equality with the first arrayBuffer
  console.log('Is the first bufferArray the same as the one we received ?', imageData.data.buffer === buf);  
  ctx.putImageData(imageData, 0, 0);
}

const ctx = canvas.getContext('2d');
ctx.fillStyle = 'green';
ctx.fillRect(20, 20, 60, 80);
let imageData = ctx.getImageData(0, 0, 300, 150);
// pass it as transferable
worker.postMessage(imageData.data.buffer);
console.log(imageData.data.length, 'not empty')
<script type="worker-script" id="workerScript">
	self.onmessage = e => {
		let buf = e.data,
		arr = new Uint8Array(buf);
		console.log('worker received', buf);
		for(let i =0; i<arr.length; i+=4){
			arr[i] = (arr[i] + 128) % 255;
			arr[i+1] = (arr[i+1] + 128) % 255;
			arr[i+2] = (arr[i+2] + 128) % 255;			
			}
		console.log(arr);
		self.postMessage(buf);
		// this won't print in stacksnippet's console
		// you have to check your dev tools' one
		console.log('worker now holds', buf.byteLength, 'full');
		};
</script>
<canvas id="canvas"></canvas>

关于javascript - getImageData - Web worker - 如何减少垃圾回收?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41722068/

相关文章:

javascript - Ext JS 存储和复杂对象

javascript - 跨浏览器事件处理程序,jQuery 不起作用

javascript - 当应用程序在后台时,React Native JS 不执行

javascript - Canvas 中的行星、月亮和剪裁弧 - 粗糙边缘

java - 咖啡因:如何得出合适的缓存大小

Json.NET 和 GC 等待的 C# 服务性能问题

javascript - jshint 错误 : Redefinition of '_'

javascript - HTML 5 Canvas addHitRegion 不适用于 iOS(未定义不是函数)

javascript - Canvas FPS 人为限制为 100fps?

java - 未使用的私有(private)变量可以在其持有实例之前被 GC 吗?