WebAssembly线性内存访问越界时为什么会崩溃?
我在用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都减半还是不行,是不是内存对齐有问题?或者线性内存的实际可用空间怎么算的?
mem.buffer的访问方式上。JS里面,当你调用
mem.grow()扩展内存后,mem.buffer并不会自动更新。也就是说,你最初拿到的mem.buffer是指向初始大小的那个内存块,后续的 grow 操作会分配新的内存,但原来的 buffer 对象还是指向老地方。所以你访问超出初始大小的 offset 时,就会触发 out of bounds 错误。正确的做法是每次 grow 之后都重新获取 buffer,比如:
另外,WebAssembly 的线性内存是以 64KB(一页)为单位管理的,初始 256 页就是 256 * 64KB = 16MB,grow 512 页之后就是 768 页,总共 48MB。你访问的时候要确保 offset + size 没有超过当前内存的总大小。
不需要考虑内存对齐的问题,因为 Uint8ClampedArray 是按字节访问的,不会有对齐要求。问题核心就是 buffer 没有更新到最新的内存块。
另外,memory.grow()返回的是新增的页数,失败会返回0,最好检查下返回值。