传输 TypedArray 和 Buffer 时的注意事项


所有 TypedArrayBuffer 实例都是对底层 ArrayBuffer 的视图。 也就是说,实际存储原始数据的是 ArrayBuffer,而 TypedArrayBuffer 对象提供了查看和操作数据的方式。 在同一个 ArrayBuffer 实例上创建多个视图是可能且常见的。 使用传输列表传输 ArrayBuffer 时必须非常小心,因为这样做会导致共享同一个 ArrayBuffer 的所有 TypedArrayBuffer 实例变得不可用。

const ab = new ArrayBuffer(10);

const u1 = new Uint8Array(ab);
const u2 = new Uint16Array(ab);

console.log(u2.length);  // 打印 5

port.postMessage(u1, [u1.buffer]);

console.log(u2.length);  // 打印 0

对于 Buffer 实例,具体来说,底层 ArrayBuffer 是否可以被传输或克隆完全取决于实例是如何创建的,这通常无法可靠地确定。

ArrayBuffer 可以用 markAsUntransferable() 标记来表示它应该总是被克隆并且永远不会被传输。

根据 Buffer 实例的创建方式,它可能拥有也可能不拥有其底层 ArrayBuffer。 除非知道 Buffer 实例拥有它,否则不得传输 ArrayBuffer。 特别是,对于从内部 Buffer 池(使用,例如 Buffer.from()Buffer.allocUnsafe())创建的 Buffer,传输它们是不可能的,它们总是被克隆,这会发送整个 Buffer 池的副本。 此行为可能会带来意想不到的更高内存使用率和可能的安全问题。

有关 Buffer 池化的更多详细信息,请参阅 Buffer.allocUnsafe()

使用 Buffer.alloc()Buffer.allocUnsafeSlow() 创建的 Buffer 实例的 ArrayBuffer 始终可以传输,但这样做会使那些 ArrayBuffer 的所有其他现有视图无法使用。

All TypedArray and Buffer instances are views over an underlying ArrayBuffer. That is, it is the ArrayBuffer that actually stores the raw data while the TypedArray and Buffer objects provide a way of viewing and manipulating the data. It is possible and common for multiple views to be created over the same ArrayBuffer instance. Great care must be taken when using a transfer list to transfer an ArrayBuffer as doing so causes all TypedArray and Buffer instances that share that same ArrayBuffer to become unusable.

const ab = new ArrayBuffer(10);

const u1 = new Uint8Array(ab);
const u2 = new Uint16Array(ab);

console.log(u2.length);  // prints 5

port.postMessage(u1, [u1.buffer]);

console.log(u2.length);  // prints 0

For Buffer instances, specifically, whether the underlying ArrayBuffer can be transferred or cloned depends entirely on how instances were created, which often cannot be reliably determined.

An ArrayBuffer can be marked with markAsUntransferable() to indicate that it should always be cloned and never transferred.

Depending on how a Buffer instance was created, it may or may not own its underlying ArrayBuffer. An ArrayBuffer must not be transferred unless it is known that the Buffer instance owns it. In particular, for Buffers created from the internal Buffer pool (using, for instance Buffer.from() or Buffer.allocUnsafe()), transferring them is not possible and they are always cloned, which sends a copy of the entire Buffer pool. This behavior may come with unintended higher memory usage and possible security concerns.

See Buffer.allocUnsafe() for more details on Buffer pooling.

The ArrayBuffers for Buffer instances created using Buffer.alloc() or Buffer.allocUnsafeSlow() can always be transferred but doing so renders all other existing views of those ArrayBuffers unusable.