跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

如何用 Transferable 对象零拷贝转移超大数组内存给子线程

使用Transferable对象可零拷贝转移ArrayBuffer所有权至Worker,主线程buffer变为null,须显式传入transferList,Worker端用新TypedArray包装接收,避免深拷贝与detached错误。 用
Transferable
对象实现零拷贝转移超大数组内存给子线程,核心是把
ArrayBuffer
的所有权移交给 Worker,主线程不再持有该缓冲区,从而避免内存复制。这在处理百万级甚至千万级
Float32Array
、
Uint8Array
等时能显著提升性能和内存效率。 确认目标数组底层是 ArrayBuffer 只有基于
ArrayBuffer
的视图(如
Uint8Array
、
Float64Array
)才能被转移。普通数组(
[]
)、
TypedArray
的拷贝副本、或未绑定 buffer 的视图都不支持。 ✅ 正确:创建时显式传入新
ArrayBuffer
❌ 错误:用
new Uint8Array(1000000)
—— 虽然看起来像,但内部 buffer 可被转移;不过更推荐显式构造以明确控制 ⚠️ 注意:
typedArray.buffer
必须是“可转移的”,即未被其他引用长期持有,且不能是
SharedArrayBuffer
(后者用于共享,不走 transfer) 使用 postMessage + transfer 参数移交所有权 调用
worker.postMessage(data, [transferList])
,把
ArrayBuffer
列在
transferList
中。移交后,主线程原数组的
.buffer
变为
null
,试图访问会报
TypeError: Cannot perform %ArrayBuffer% constructor on a detached ArrayBuffer
。 正确写法:
worker.postMessage({arr: myUint8Array}, [myUint8Array.buffer])
也可以直接传 buffer:
worker.postMessage(myUint8Array.buffer, [myUint8Array.buffer])
,Worker 内再用
new Uint8Array(buf)
包装 一次可转移多个 buffer:
[buf1, buf2, otherArrayBuffer]
Worker 端接收并重建视图 在 Worker 的
onmessage
回调中,从
event.data
拿到 transferred buffer,然后用它构造新的 TypedArray。此时内存地址不变,只是 JS 引用切换了上下文。 示例:
self.onmessage = function(e) { const buf = e.data; const arr = new Float32Array(buf); // 零拷贝重建 // 后续计算直接操作 arr,无需复制 };
注意:不能用
e.data.buffer
—— 因为
e.data
就是 buffer 本身(若你直接 post buffer),或需解构取
e.data.arr.buffer
移交后无法再在主线程读写该 buffer,这是强制的内存安全机制 常见陷阱与验证方法 零拷贝容易因小疏忽退化为拷贝,可通过内存占用和时间对比验证是否生效。 ❌ 忘记加 transfer 列表 → 自动深拷贝,大数组卡死或 OOM ❌ 主线程在 postMessage 后还访问
arr[0]
或
arr.length
→ 报 detached 错误 ✅ 验证方式:Chrome DevTools Memory 面板中观察主线程 ArrayBuffer 是否消失;或用
performance.now()
测 postMessage 耗时 —— 几 MB 以下应 ? 提示:Node.js 的
Worker
同样支持 transfer,语法一致

相关文章