文章中如果有图看不到,可以点这里去 csdn 看看。从那边导过来的,文章太多,没法一篇篇修改好。

浏览器内部如何使用共享内存?

浏览器中使用共享内存(Shared Memory)主要是为了在多个执行上下文(线程、Worker、甚至不同页面)之间高效地共享数据,而不需要复制(zero-copy)。这在性能敏感、并发计算密集或实时性要求高的场景中特别重要。


一、背景:浏览器中的并发执行模型

浏览器中的 JavaScript 是单线程的(主线程 + 事件循环),为了避免阻塞 UI,浏览器引入了:

  • Web Worker(后台线程)
  • SharedWorker(多个页面共享)
  • ServiceWorker(网络代理层)
  • WebAssembly 线程

这些 Worker 拥有独立的内存空间,与主线程之间通常通过 postMessage() 进行通信。

但是 postMessage 会 复制 数据(结构化克隆),当数据量大或频繁传输时非常慢。


二、共享内存(Shared Memory)的核心机制

1. SharedArrayBuffer(SAB)

  • SharedArrayBuffer 是浏览器中共享内存的核心对象。
  • 它允许多个线程(例如主线程 + Web Worker)访问同一块二进制内存
  • 数据不会复制,访问的是同一个物理缓冲区。
// 主线程
const sab = new SharedArrayBuffer(1024);
const arr = new Int32Array(sab);

const worker = new Worker("worker.js");
worker.postMessage(sab);  // 传引用,不复制
// worker.js
onmessage = (e) => {
  const arr = new Int32Array(e.data);
  arr[0] = 42; // 主线程也能看到变化
};

2. Atomics 原子操作

由于多个线程可以同时访问同一块内存,浏览器提供 Atomics 对象用于:

  • 安全地读写共享内存中的数据(原子性保证)
  • 实现同步机制(例如锁、自旋、信号量)
Atomics.store(arr, 0, 123);
const value = Atomics.load(arr, 0);
Atomics.add(arr, 1, 1);
Atomics.wait(arr, 2, 0);
Atomics.notify(arr, 2, 1);

这些操作可以避免竞争条件(race condition)。


三、共享内存的典型用途

1. 多线程并行计算

在 WebAssembly 或 Web Worker 中,把数据块放入 SharedArrayBuffer 中,由多个 Worker 并行处理。

例如图像处理、视频解码、AI 模型推理:

主线程 -> 分配 SAB -> 多个 Worker 并行处理像素块 -> 主线程合并结果

这样可以充分利用多核 CPU。


2. 实时数据共享

在游戏、音频、视频流等场景中,需要低延迟共享数据:

  • 主线程负责渲染或播放
  • Worker 负责解码、数据准备
  • 双方通过 SAB 实时共享帧数据或音频样本

3. WebAssembly 并发

WebAssembly 线程(使用 SharedArrayBuffer)可以像原生程序一样多线程运行。

比如在浏览器中运行 C/C++ 编译的物理引擎、机器学习框架时,会使用共享内存来进行线程间通信。


4. 浏览器内数据库或缓存共享

例如在同源的多个 tab 或 worker 之间共享大对象缓存(比如模型权重、纹理数据)。

以前做法是 IndexedDB + fetch + 再复制,现在用 SAB 可以让不同 Worker 直接访问相同的数据段,极大节省内存和加载时间。


四、安全限制与 COOP/COEP

由于 Spectre 类漏洞,SharedArrayBuffer 曾被禁用(2018年后重新引入,但受限制)。

要使用 SAB,网页必须启用:

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

简称 COOP/COEP,以确保隔离性和安全。


五、现实中的典型应用场景

场景说明技术要点
WebAssembly 多线程TensorFlow.js / OpenCV.jsSAB + Atomics
音视频处理实时混音、帧缓冲共享SAB + AudioWorklet
游戏引擎物理模拟、AI 逻辑线程SAB + Worker Pool
数据可视化后台计算聚合数据SAB + WebWorker
WebRTC 数据通道低延迟传输共享缓冲SAB + Atomics

六、共享内存总结

功能点含义
SharedArrayBuffer共享内存对象,允许多个线程访问同一块内存
Atomics提供线程安全的原子操作与同步机制
优势无复制、高性能、低延迟
限制必须启用安全头(COOP/COEP)
应用WebAssembly 并行计算、音视频处理、AI、游戏引擎、数据分析

在这里插入图片描述


七、为什么在共享内存较少的情况下,浏览器会报错 net::ERR_INSUFFICIENT_RESOURCES

🧩 一、先明确问题:

这里说的“共享内存太少”通常指:

  • SharedArrayBuffer 的大小太小(例如几 KB)
  • 或系统允许的共享内存总量(OS 层限制)不足
  • 或者浏览器线程池、信号量、同步等待队列资源不够

这些都会触发内部的 “resource exhaustion” 机制,从而出现统一的错误码:
**net::ERR_INSUFFICIENT_RESOURCES**


🧠二、SharedArrayBuffer 并不是简单的 “数组”

创建 SAB 时,浏览器要向操作系统申请一块 共享内存页(Shared Memory Segment),这通常通过 mmap / shm_open / CreateFileMapping 实现。

即使你只申请了很小的空间(比如 1KB),浏览器仍要:

  1. 建立共享内存对象;
  2. 维护安全标识(COOP/COEP 隔离);
  3. 在主线程和 Worker 间注册共享句柄;
  4. 分配同步结构(Atomics.wait/notify 需要 futex-like 机制)。

这些元数据结构通常比数据本身更大。

换句话说:SAB 小并不代表系统负担小。


⚙️三、当 SAB 太小或数量太多时的问题

🧱 1. 内存页粒度限制

现代系统(尤其是 Linux / macOS)分配共享内存是按页(通常 4KB 或 64KB)为单位。
每个 SAB 不管多小,至少占用一个内存页,再加上额外的句柄/描述符。

如果你频繁创建许多“小 SAB”,例如:

for (let i = 0; i < 20000; i++) {
  new SharedArrayBuffer(1024); // 1KB × 20000
}

看似只用 20MB,但实际上:

  • 需要 20,000 个内核级共享段;
  • 每个段都要内核对象、句柄、同步元数据;
  • 最终耗尽浏览器进程的文件描述符或共享内存句柄上限。

这时 Chromium 就会抛出:

net::ERR_INSUFFICIENT_RESOURCES

🧩 2. futex / wait queue 资源不足

如果你在大量 SAB 上使用 Atomics.wait() / Atomics.notify() 实现同步,浏览器会为每个 SAB 分配对应的 futex-like 等待队列。

当等待队列太多或资源池耗尽,也会触发相同错误码。
(V8 会尝试 fallback,但 Chrome 层仍会报 net::ERR_INSUFFICIENT_RESOURCES)


🧩 3. 浏览器的安全隔离上下文限制

启用了 COOP/COEP 后,每个共享内存区域都要经过 “MemoryCoordinator” 注册,
以防止跨源泄漏和 Spectre 攻击。

当浏览器认为页面的“共享内存句柄”过多、或系统共享区不足时,会提前拒绝分配。

这也是 ERR_INSUFFICIENT_RESOURCES 出现的根源之一 —— 浏览器主动拒绝创建新的共享段


📉 四、典型触发场景

场景问题描述错误原因
创建大量小 SAB每个 SAB 占 1 页,句柄爆炸句柄/文件描述符耗尽
Worker 循环中频繁创建 SAB无复用共享段共享内存区耗尽
频繁使用 Atomics.wait/notify等待队列太多futex 资源耗尽
WASM 多线程小块共享内部内存碎片过多区域分配失败
浏览器低资源模式(移动端)系统共享页不足操作系统拒绝分配

💡 五、正确做法与优化建议

  1. 复用一块大 SAB,不要创建很多小的。
const sab = new SharedArrayBuffer(1024 * 1024); // 一次分配 1MB
const views = [];
for (let i = 0; i < 100; i++) {
  views.push(new Int32Array(sab, i * 1024, 256));
}

这样只占一个共享段,降低句柄压力。

  1. 控制 Worker 数量:线程数量过多也会导致资源消耗放大。
  2. 避免短生命周期 SAB:频繁创建/销毁共享内存比复用更容易触发内存泄漏。
  3. 监控浏览器内存分配:使用 chrome://tracingchrome://memory-internals/ 检查 SharedArrayBuffer 分配。
  4. 合理粒度:推荐每个 SAB 至少 64KB,避免页碎片。

🧭 六、总结

项目含义
错误本质浏览器资源(共享内存页、句柄、队列)耗尽
触发条件小块 SAB 太多 / futex 太多 / 系统共享段不足
错误码含义Chromium 统一“资源耗尽”错误,而非真正网络错误
解决方案合并小块、复用内存、减少 SAB 数量

在这里插入图片描述

posted @ 2025-10-15 10:56  NeoLshu  阅读(0)  评论(0)    收藏  举报  来源