You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何释放导出到JavaScript的WASM堆内存

如何释放导出到JavaScript的WASM堆内存

嘿,看你在尝试把WASM堆上分配的数据导出到JavaScript,还在头疼怎么安全释放内存避免泄漏吧?我给你梳理下正确的做法,都是踩过坑的过来人经验~

先说说你现有代码的问题

你定义的ArrayBuffer结构体只暴露了指针和长度,但完全没处理内存释放的关键信息——Rust的dealloc函数不仅需要指针,还需要内存的布局(Layout)(包含大小和对齐规则)。而且直接把裸指针暴露给JS后,要是JS侧没正确触发释放,WASM堆里的这块内存就永远泄漏了。

手动管理内存的正确实现

如果一定要手动控制内存分配和释放,得把布局信息存在结构体里,再加一个给JS调用的释放方法,完整代码如下:

use wasm_bindgen::prelude::*;
use std::alloc::{alloc, dealloc, Layout};
use std::ptr;

#[wasm_bindgen]
pub struct ArrayBuffer {
    ptr: *mut u8,
    len: usize,
    layout: Layout, // 保存内存布局,释放时必须用到
}

#[wasm_bindgen]
impl ArrayBuffer {
    // 示例:创建一个指定长度的堆分配缓冲区
    pub fn new(len: usize) -> Self {
        // 创建内存布局
        let layout = Layout::array::<u8>(len).expect("Failed to create memory layout");
        // 执行堆分配
        let ptr = unsafe { alloc(layout) };
        if ptr.is_null() {
            panic!("Memory allocation failed");
        }
        // 可选:初始化缓冲区数据(比如全部填0)
        unsafe {
            ptr::write_bytes(ptr, 0, len);
        }
        ArrayBuffer { ptr, len, layout }
    }

    // 暴露给JS获取指针(仅在必须时使用,不推荐直接操作裸指针)
    #[wasm_bindgen(getter)]
    pub fn ptr(&self) -> *mut u8 {
        self.ptr
    }

    // 暴露给JS获取缓冲区长度
    #[wasm_bindgen(getter)]
    pub fn len(&self) -> usize {
        self.len
    }

    // 核心:给JS调用的释放方法,命名成JS习惯的free
    #[wasm_bindgen(js_name = free)]
    pub fn free(&mut self) {
        // 避免重复释放野指针
        if !self.ptr.is_null() {
            unsafe {
                dealloc(self.ptr, self.layout);
                // 释放后置空指针,防止二次调用free时出错
                self.ptr = ptr::null_mut();
            }
        }
    }
}

在JavaScript侧,你需要这样使用,用完必须主动调用free:

// 创建WASM侧的缓冲区
const buffer = Module.ArrayBuffer.new(1024);
// 用buffer.ptr和buffer.len做你需要的操作
// ...
// 用完一定要释放!
buffer.free();

更推荐的安全简化方案

其实wasm-bindgen已经帮我们封装了更安全的跨类型转换,完全不需要手动管理指针。比如直接用Rust的Vec转成JS的Uint8Array,内存会由JS的垃圾回收和WASM的绑定自动管理:

use wasm_bindgen::prelude::*;
use js_sys::Uint8Array;

#[wasm_bindgen]
pub fn create_safe_buffer(len: usize) -> Uint8Array {
    // 用Rust的Vec自动管理堆内存
    let mut data = vec![0u8; len];
    // 在这里对data做任何你需要的修改
    data[0] = 255; // 示例操作
    // 直接转成JS的Uint8Array,wasm-bindgen会处理内存所有权
    Uint8Array::from(&data)
}

这种方式下,当JS侧的Uint8Array被垃圾回收时,对应的WASM堆内存也会被自动释放,完全不用你手动写释放逻辑,安全又省心。

关键注意事项

  • 永远不要在释放后再使用指针:释放后一定要把指针置空,避免野指针访问导致崩溃。
  • 手动分配和释放必须用同一个Layout:如果创建时的Layout和释放时的不一致,会触发未定义行为,直接搞崩WASM实例。
  • 能不用裸指针就不用:除非你有特别的底层操作需求,否则优先用wasm-bindgen提供的安全类型转换,减少出错概率。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 13:13:03