你只能通过重用像素数据数组来减少每个 JS 上下文中的整体 GC 命中,但你不能做出重大改变。问题在于与工作人员之间传输的数据。
当您向工作人员发布数据时,工作人员必须分配内存来接收该数据。它不能说“这是我已经在使用的一些 RAM,请把它放在这里”。遗憾的是,没有,每次发布的消息及其内容都会作为新对象到达工作人员的上下文。这同样适用于返回的数据,每条消息都是一条新消息,具有新的引用、分配和最终删除。
您可以一次发送较小的数据块,这可能会分散 GC 命中,因此您不会收到大的 GC 峰值。但内存使用量与内存吞吐量相关,除非你降低吞吐量,否则不会降低 GC 负载。
您可能想看看sharedArrayBuffers,因为它们提供了 Javascript 线程之间的共享内存资源。当前支持的是 Chrome(需要标志)和 Firefox。但是需要推动这种类型的内存管理发生,因此值得您尝试一下。
SharedArrayBuffers 应该可以在很大程度上消除此类应用程序的 GC 命中。
更新:
根据新信息,您可以尝试在 worker.postMessage 调用中使用 transferable transfer 参数。请参阅 W3C 草案worker postmessage 以发布到工作人员,worker global scope postmessage 以从工作人员返回数据。
此处定义了可转移对象transferable objects ,它指出您只能转移一个对象一次。当工作人员收到对象时,它不能将其作为可转移对象返回(根据文档)。如果您希望将其作为可转移对象返回,则必须从收到的任何数组中创建一个新的类型化数组。
文档中不清楚的是,这如何影响发送者上下文中的内存管理。
更新 2
在使用可传输数据后,我发现要停止任何内存开销,您需要拥有两个数据副本(在 imageData 的情况下)
保存像素数据的 imageData.data 属性在传输后无法使用。您需要创建一个新的 imageData 数组或复制数据以发送到另一个数组并在使用 typedArray set 函数返回时将其复制回来。
以下是一个示例(代码位不是全部),它使用可传输数据在工作人员之间传输数据,同时不会产生过度的 GC 开销。在 Chrome 上运行类似的代码并检查时间线显示没有超过 0.002 毫秒的 GC 命中
// one time set up
var imgData = ctx.getImageData(0, 0, 512, 512);
var tData = new Uint8ClampedArray(512 * 512 * 4);
tData.set(imgData.data);
// repeats from here
worker.sendMessage(tData.buffer, [tData.buffer]);
// In the worker
onmessage(event){
var data = new Uint8ClampedArray(event.data);
// process the data
// return data
postMessage(event.data,[event.data]);
}
// Back on the main thread
onmessage(event){
tData = new Uint8ClampedArray(event.data);
imgData.data.set(tData);
ctx.putImageData(imgdata, 0, 0);
}
// now you can resend tData as it is a new typedArray referance (to the same data)