强制节点中的垃圾回收以测试WeakRef和FinalizationRegistry

2022-06-15 00:00:00 node.js garbage-collection javascript v8

我正在玩WeakRef and FinalizationRegistry in V8,我无法验证以下代码在Node.js中是否可以正常工作。

我使用的是Node v15.3.0,运行方式如下:

node --expose-gc transient.js

我希望在控制台日志中看到一些finalizerCallback called!条目。

如果我在基于Chromium浏览器(尝试Run code snippet按钮)中运行它,当异步脚本仍在运行时,如果在DevTools(在Chrome v87中)的Performance部分中单击垃圾桶图标(收集垃圾),则可以获得输出。

它在Firefox v83中按预期工作,当脚本结束时,我看到所有终结器回调。

使用Node时,我希望在显式调用垃圾回收时自动看到它,但根本不会调用终结器回调。

代码本身是否有问题,或者是否有更可靠的方法在节点中强制GC?

数据-lang="js"数据-隐藏="假"数据-控制台="真"数据-巴贝尔="假">
// by @noseratio
// see https://v8.dev/features/weak-references

class Transient {
  constructor(eventTarget) {
    const finalizerCallback = ({ eventTarget, eventListener }) => {
      console.log('finalizerCallback called!');
      eventTarget.removeEventListener('testEvent', eventListener);
    }
    const finalizer = new FinalizationRegistry(finalizerCallback);

    const strongRefs = { finalizer };
    const weakRef = new WeakRef(this);
    const eventListener = () => {
      console.log('eventListener called!');
      strongRefs.finalizer = null;
      weakRef.deref()?.test();
    }

    finalizer.register(this, { eventTarget, eventListener });

    eventTarget.addEventListener('testEvent', eventListener, { once: true });
  }

  test() {
    console.log("test called!");
  }
}

async function main() {
  const gc = globalThis?.global?.gc;
  console.log(`garbage collector func: ${gc}`);

  const eventTarget = new EventTarget();

  for (let i = 10; i > 0; i--) {
    void function () {
      // these instances of Transient really must be getting GC'ed!
      new Transient(eventTarget);
    }();
  
    await new Promise(r => setTimeout(() => r(gc?.(true)), 100));
  }

  console.log("finishing...")
  gc?.(true);
  await new Promise(r => setTimeout(r, 5000));

  eventTarget.dispatchEvent(new Event("testEvent"));
  console.log("done.")
}

main().catch(console.error);

已更新,如果我增加for循环集成的数量,我最终会看到一些finalizerCallback调用,但它们仍然是零星的。


解决方案

我直接从@jasnell获取:

您可以尝试使用本机语法函数%CollectGarbage

我试过了,它完全按照我想要的方式工作。在现实生活中,它是一个AbortController对象链,更多上下文here。

启用%CollectGarbage

node --allow-natives-syntax Transient.js

要向静态代码分析器隐藏%语法,我们可以使用eval

eval("%CollectGarbage('all')");

相关文章