SharedArrayBuffer
BaselineWidely available *
This feature is well established and works across many devices and browser versions. It’s been available across browsers since December 2021.
* Some parts of this feature may have varying levels of support.
SharedArrayBuffer
对象用来表示一个通用的原始二进制数据缓冲区,类似于ArrayBuffer
对象,但它可以用来在共享内存上创建视图。与可转移的ArrayBuffer
不同,SharedArrayBuffer
不是可转移对象。
描述
要在集群中的一个代理(agent,可以是网页的主程序或其任意一个 web worker)与另一个代理之间使用ShareArrayBuffer
共享内存,需要使用postMessage
和结构化克隆。
结构化克隆算法接受SharedArrayBuffer
对象和映射到SharedArrayBuffer
对象的类型化数组。在这两种情况下,SharedArrayBuffer
对象会被传输给接收者,从而在接收代理中产生一个新的、私有的SharedArrayBuffer
对象(就像ArrayBuffer
一样)。但是,两个SharedArrayBuffer
对象指向的共享数据块其实是同一个数据块,一个代理中对数据块的修改最终会将在另一个代理中可见。
const sab = new SharedArrayBuffer(1024);worker.postMessage(sab);
共享内存可以被 worker 线程或主线程创建和同时更新。根据系统(CPU、操作系统、浏览器)的不同,需要一段时间才能将变化传递给所有上下文环境。因此需要通过原子操作来进行同步。
SharedArrayBuffer
被一些 web API 使用,比如:
安全需求
由于幽灵漏洞,共享内存和高精度定时器在 2018 年 1 月 5 日开始被禁用。在 2020 年,一种新的、安全的方法已经被标准化,以重新启用共享内存。
作为基本要求,你的文档需要处于一个安全上下文中。
对于顶级文档,需要设置两个标头来实现你网站的跨源隔离:
Cross-Origin-Opener-Policy
设置为same-origin
(来保护你的源站点免受攻击)Cross-Origin-Embedder-Policy
设置为require-corp
或credentialless
(保护受害者免受你的源站点的影响)
Cross-Origin-Opener-Policy: same-originCross-Origin-Embedder-Policy: require-corp
为了验证跨源隔离是否生效,你可以测试窗口和 worker 上下文中的Window.crossOriginIsolated
或WorkerGlobalScope.crossOriginIsolated
属性:
const myWorker = new Worker("worker.js");if (crossOriginIsolated) { const buffer = new SharedArrayBuffer(16); myWorker.postMessage(buffer);} else { const buffer = new ArrayBuffer(16); myWorker.postMessage(buffer);}
在设置了这两个标头后,postMessage()
不再为SharedArrayBuffer
对象抛出错误,因此,跨线程共享内存现在可用。
嵌套文档和专用 worker 线程也需要将Cross-Origin-Embedder-Policy
标头设置为同样的值。对于同源嵌套文档和子资源,不需要进行任何其他更改。同站(但跨源)嵌套文档和子资源需要将Cross-Origin-Resource-Policy
标头设置为same-site
。而它们的跨源(和跨站点)的对应部分也需要将同样的标头设置为cross-origin
。请注意,将Cross-Origin-Resource-Policy
标头设置为除same-origin
之外的任何值,都会使资源暴露于潜在的攻击中,比如幽灵漏洞。
请注意,Cross-Origin-Opener-Policy
标头会限制你对弹出窗口引用的保留能力。两个顶级窗口上下文之间的直接访问基本上只在它们同源且携带相同的两个标头(且具有相同的值)时才可行。
API 可用性
根据是否采取了上述安全措施,各类内存共享 API 具有不同的可用性:
Atomics
对象总是可用的。SharedArrayBuffer
对象在原则上始终可用,但遗憾的是,除非设置了前面提到的两个标头,否则其在全局对象上的构造函数是隐藏的,这是为了兼容 web 内容。这个限制有望在未来被移除。尽管如此,仍然可以用WebAssembly.Memory
来获取实例。- 除非设置了上文提到的两个标头,否则各种
postMessage()
的 API 在处理SharedArrayBuffer
对象时会抛出异常。如果正确设置了这两个标头,Window
对象和专用 worker 线程上的postMessage()
都可以正常工作,并允许跨线程共享内存。
WebAssembly 共享内存
WebAssembly.Memory
对象可以通过设置shared
构造函数标志来创建。当这个标志设置为true
时,构造出的Memory
对象就像SharedArrayBuffer
一样,可以通过postMessage()
在 worker 线程之间共享,而且Memory
对象的后备buffer
是一个SharedArrayBuffer
。因此,上述关于在 worker 线程间共享 SharedArrayBuffer 的要求同样适用于共享WebAssembly.Memory
。
WebAssembly Thread 提案还定义了一套新的原子指令。就像SharedArrayBuffer
及其方法始终可用(并且只有在设置了新标头的情况下,才允许线程间共享)一样,WebAssembly 原子指令也是始终可用的。
增大 SharedArrayBuffer
SharedArrayBuffer
对象可以通过在调用SharedArrayBuffer()
时包含maxByteLength
选项来使其可增大。你可以通过访问SharedArrayBuffer
的growable
和maxByteLength
属性来分别查询其是否可增大以及其最大大小。你还可以通过调用grow()
为一个可增大的SharedArrayBuffer
分配新的大小。新字节被初始化为 0。
这些特性令增大SharedArrayBuffer
更为高效——否则,你必须创建一个新大小的缓冲区副本。它还使得 JavaScript 在这方面与 WebAssembly 保持一致(Wasm 线性内存可以通过WebAssembly.Memory.prototype.grow()
调整大小)。
出于安全原因,SharedArrayBuffer
的大小无法缩小,只能增大。
构造函数
SharedArrayBuffer()
创建一个新的
SharedArrayBuffer
对象。
静态属性
SharedArrayBuffer[Symbol.species]
返回用于构造
SharedArrayBuffer
方法返回值的构造函数。
实例属性
属性定义于SharedArrayBuffer.prototype
并且被所有SharedArrayBuffer
实例所共享。
SharedArrayBuffer.prototype.byteLength
数组大小,以字节为单位。在构造数组时被确定,并且只能在可增大的
SharedArrayBuffer
上通过SharedArrayBuffer.prototype.grow()
方法来改变。SharedArrayBuffer.prototype.constructor
创建实例对象的构造函数。对于
SharedArrayBuffer
实例,其初始值为SharedArrayBuffer
构造函数。SharedArrayBuffer.prototype.growable
只读。如果当前
SharedArrayBuffer
可以增大,则返回true
,否则返回false
。SharedArrayBuffer.prototype.maxByteLength
当前
SharedArrayBuffer
可以增大的最大长度,只读,以字节为单位。在构造数组时确定且无法更改。SharedArrayBuffer.prototype[Symbol.toStringTag]
[Symbol.toStringTag]
属性的初始值是字符串"SharedArrayBuffer"
。它被用于Object.prototype.toString()
。
实例方法
SharedArrayBuffer.prototype.grow()
增大当前
SharedArrayBuffer
到指定大小,以字节为单位。SharedArrayBuffer.prototype.slice()
返回一个新的
SharedArrayBuffer
,其内容是当前SharedArrayBuffer
从begin
(含)到end
(不含)的字节的副本。如果begin
或end
为负,则它是从数组的末尾开始的索引,而不是数组的开头。
示例
创建一个新的 SharedArrayBuffer
const sab = new SharedArrayBuffer(1024);
截取 SharedArrayBuffer
sab.slice(); // SharedArrayBuffer { byteLength: 1024 }sab.slice(2); // SharedArrayBuffer { byteLength: 1022 }sab.slice(-2); // SharedArrayBuffer { byteLength: 2 }sab.slice(0, 1); // SharedArrayBuffer { byteLength: 1 }
在 WebGL buffer 中使用
const canvas = document.querySelector("canvas");const gl = canvas.getContext("webgl");const buffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, buffer);gl.bufferData(gl.ARRAY_BUFFER, sab, gl.STATIC_DRAW);
规范
Specification |
---|
ECMAScript® 2026 Language Specification # sec-sharedarraybuffer-objects |
浏览器兼容性
参见
Atomics
ArrayBuffer
- JavaScript 类型化数组指南
- Web Workers
- 共享内存——简明教程,TC39 ecmascript-sharedmem 提案
- JavaScript 新并发原语的初体验,hacks.mozilla.org(2016)
- COOP 和 COEP 的解释,由 Chrome 团队撰写(2020)
Cross-Origin-Opener-Policy
Cross-Origin-Embedder-Policy
Cross-Origin-Resource-Policy
Window.crossOriginIsolated
和WorkerGlobalScope.crossOriginIsolated
- Android Chrome 88 和桌面版 Chrome 92 中的 SharedArrayBuffer 更新,developer.chrome.google.cn(2021)