WebAssembly线性内存访问越界时为什么会崩溃?

百里远香 阅读 47

我在用WebAssembly处理图片数据时遇到奇怪的问题,当通过memory.grow()扩展线性内存后,访问特定地址就会导致页面崩溃。明明计算过内存大小了,比如这样设置:


const mem = new WebAssembly.Memory({initial: 256, maximum: 1024});
// 导入对象后调用
await mem.grow(512); // 扩展到768页

然后在JS里这样访问像素数据:new Uint8ClampedArray(mem.buffer, offset, size),但运行时提示“out of bounds”错误。试过把offset和size都减半还是不行,是不是内存对齐有问题?或者线性内存的实际可用空间怎么算的?

我来解答 赞 14 收藏
二维码
手机扫码查看
2 条解答
码农子慧
你这个问题很常见,很多刚接触 WebAssembly 的同学都会踩坑。问题出在 mem.buffer 的访问方式上。

JS里面,当你调用 mem.grow() 扩展内存后,mem.buffer 并不会自动更新。也就是说,你最初拿到的 mem.buffer 是指向初始大小的那个内存块,后续的 grow 操作会分配新的内存,但原来的 buffer 对象还是指向老地方。所以你访问超出初始大小的 offset 时,就会触发 out of bounds 错误。

正确的做法是每次 grow 之后都重新获取 buffer,比如:

await mem.grow(512);
const buffer = mem.buffer;
const array = new Uint8ClampedArray(buffer, offset, size);


另外,WebAssembly 的线性内存是以 64KB(一页)为单位管理的,初始 256 页就是 256 * 64KB = 16MB,grow 512 页之后就是 768 页,总共 48MB。你访问的时候要确保 offset + size 没有超过当前内存的总大小。

不需要考虑内存对齐的问题,因为 Uint8ClampedArray 是按字节访问的,不会有对齐要求。问题核心就是 buffer 没有更新到最新的内存块。
点赞 5
2026-02-04 10:17
FSD-梓睿
你这个问题是没搞清楚WebAssembly内存单位。Memory的单位是64KB页,不是字节,grow的时候也要按页来算。扩展到768页后,总大小是768 * 64KB = 48MB,确保你的offset + size不超过这个值就行。

const memSizeInBytes = 768 * 65536; // 计算总字节数
if (offset + size > memSizeInBytes) {
console.error("访问越界了!");
}


另外,memory.grow()返回的是新增的页数,失败会返回0,最好检查下返回值。
点赞 6
2026-02-02 03:00