强制在 Node 中进行垃圾收集以测试 WeakRef 和 FinalizationRegistry
Forcing garbage collection in Node to test WeakRef and FinalizationRegistry
我正在玩 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 中),而异步脚本仍然是 运行ning.
它在 Firefox v83 中按预期工作,我在脚本结束时看到所有终结器回调。
对于 Node,我希望在我显式调用垃圾收集时自动看到它,但根本不会调用终结器回调。
是代码本身有问题,还是有更靠谱的方式在Node中强制GC?
// 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
调用,但它们仍然零星的。
我有 the answer directly from @jasnell:
You could try the natives syntax function %CollectGarbage
我试过了,它完全符合我的要求。在现实生活中,它是 AbortController
个对象的链,更多上下文 here。
启用%CollectGarbage
:
node --allow-natives-syntax Transient.js
要在静态代码分析器中隐藏 %
语法,我们可以使用 eval
:
eval("%CollectGarbage('all')");
我正在玩 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 中),而异步脚本仍然是 运行ning.
它在 Firefox v83 中按预期工作,我在脚本结束时看到所有终结器回调。
对于 Node,我希望在我显式调用垃圾收集时自动看到它,但根本不会调用终结器回调。
是代码本身有问题,还是有更靠谱的方式在Node中强制GC?
// 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
调用,但它们仍然零星的。
我有 the answer directly from @jasnell:
You could try the natives syntax function
%CollectGarbage
我试过了,它完全符合我的要求。在现实生活中,它是 AbortController
个对象的链,更多上下文 here。
启用%CollectGarbage
:
node --allow-natives-syntax Transient.js
要在静态代码分析器中隐藏 %
语法,我们可以使用 eval
:
eval("%CollectGarbage('all')");