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

Weak引用未触发值释放:Wasm Bindgen Drop测试失败

问题原因

  1. FinalizationRegistry回调的异步性:wasm-bindgen依赖JavaScript的FinalizationRegistry触发Rust对象的Drop逻辑。当JS对象被垃圾回收后,FinalizationRegistry的回调是异步调度的,不会在调用window.gc()后立即执行。你的测试在调用gc()后立刻断言,此时Drop逻辑还未执行,导致断言失败。
  2. Chrome Headless的GC配置缺失:默认情况下Chrome Headless可能未启用window.gc()的暴露,或GC策略不会立即回收无引用对象,导致GC调用未生效。
  3. 潜在的引用残留:测试运行环境(如wasm-bindgen-test的运行时)可能意外保留了JS对象的引用,阻碍垃圾回收触发。

解决办法

1. 修改测试代码,等待异步Drop回调执行

利用Promise和异步等待逻辑,确保Drop完成后再执行断言:

use std::sync::atomic::{AtomicBool, Ordering};
use std::mem;
use wasm_bindgen::JsValue;
use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};
use wasm_bindgen_futures::JsFuture;
use js_sys::{Promise, Function};

wasm_bindgen_test_configure!(run_in_browser);

// 将static变量移到函数外部,避免测试状态污染
static DROPPED: AtomicBool = AtomicBool::new(false);

struct SetOnDrop;
impl Drop for SetOnDrop {
    fn drop(&mut self) {
        let is_dropped = DROPPED.swap(true, Ordering::AcqRel);
        assert!(!is_dropped);
    }
}

#[wasm_bindgen]
struct WasmSetOnDrop(SetOnDrop);

#[wasm_bindgen_test]
async fn test_drop() {
    // 重置状态,确保每次测试都是干净的
    DROPPED.store(false, Ordering::Release);

    let value = WasmSetOnDrop(SetOnDrop);
    let value = JsValue::from(value);

    // 第一次GC验证对象未被提前回收
    js_sys::eval("window.gc()").expect("Unable to call `window.gc`");
    assert!(!DROPPED.load(Ordering::Acquire), "Value was dropped early");

    // 丢弃Rust侧的JS引用
    mem::drop(value);

    // 触发GC
    js_sys::eval("window.gc()").expect("Unable to call `window.gc`");

    // 创建Promise,轮询等待Drop完成
    let promise = Promise::new(&mut |resolve, _reject| {
        let check = move || {
            if DROPPED.load(Ordering::Acquire) {
                resolve.call1(&JsValue::undefined(), &JsValue::from(true)).unwrap();
            } else {
                // 每隔10ms检查一次,最多等待5秒
                web_sys::window().unwrap().set_timeout_with_callback_and_timeout_and_arguments_0(
                    &Function::new_no_args(&check),
                    10,
                ).unwrap();
            }
        };
        check();
    });

    // 等待Promise完成,超时则测试失败
    JsFuture::from(promise).await.expect("Timeout waiting for drop");

    // 最终断言
    assert!(DROPPED.load(Ordering::Acquire), "Value was not dropped");
}

2. 完善Chrome Headless运行参数

修改测试命令,添加Chrome启动参数确保GC可用且行为可靠:

WASM_BINDGEN_WEAKREF=1 WASM_BINDGEN_EXTERNREF=1 wasm-pack test --chrome --headless -- --chrome-arg=--js-flags="--expose-gc" --chrome-arg=--enable-precise-memory-info

3. 排除环境引用残留

  • 确保测试对象未被赋值给全局变量(如window属性)
  • 避免测试代码中其他可能持有JS对象引用的逻辑

内容的提问来源于stack exchange,提问作者Filipe Rodrigues

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 02:35:59