diff --git a/test/common/gc.js b/test/common/gc.js index 9860bb191ee072..0c1dce291173c1 100644 --- a/test/common/gc.js +++ b/test/common/gc.js @@ -75,7 +75,43 @@ async function runAndBreathe(fn, repeat, waitTime = 20) { } } +// This requires --expose-internals +async function checkIfCollectableByCounting(fn, klass, count, waitTime = 20) { + const { internalBinding } = require('internal/test/binding'); + const { countObjectsWithPrototype } = internalBinding('heap_utils'); + const { prototype, name } = klass; + let initialCount = countObjectsWithPrototype(prototype); + console.log(`Initial count of ${name}: ${initialCount}`); + let totalCreated = 0; + for (let i = 0; i < count; ++i) { + const created = await fn(i); + totalCreated += created; + console.log(`#${i}: created ${created} ${name}, total ${totalCreated}`); + await wait(waitTime); // give GC some breathing room. + const currentCount = countObjectsWithPrototype(prototype); + const collected = totalCreated - (currentCount - initialCount); + console.log(`#${i}: counted ${currentCount} ${name}, collected ${collected}`); + if (collected > 0 ) { + console.log(`Detected ${collected} collected ${name}, finish early`); + return; + } + } + + await wait(waitTime); // give GC some breathing room. + const currentCount = countObjectsWithPrototype(prototype); + const collected = totalCreated - (currentCount - initialCount); + console.log(`Last count: counted ${currentCount} ${name}, collected ${collected}`); + // Some objects with the prototype can be collected. + if (collected > 0) { + console.log(`Detected ${collected} collected ${name}`); + return; + } + + throw new Error(`${name} cannot be collected`); +} + module.exports = { checkIfCollectable, runAndBreathe, + checkIfCollectableByCounting, }; diff --git a/test/parallel/parallel.status b/test/parallel/parallel.status index 838d465b596f9f..98288c5c1228c2 100644 --- a/test/parallel/parallel.status +++ b/test/parallel/parallel.status @@ -5,9 +5,6 @@ prefix parallel # sample-test : PASS,FLAKY [true] # This section applies to all platforms -# https://github.com/nodejs/node/pull/50327 -# Currently there's no reliable way to test it. -test-diagnostics-channel-memory-leak: SKIP [$system==win32] # https://github.com/nodejs/node/issues/41206 diff --git a/test/parallel/test-diagnostics-channel-memory-leak.js b/test/parallel/test-diagnostics-channel-memory-leak.js index 9e6364d168562f..fe50158070554d 100644 --- a/test/parallel/test-diagnostics-channel-memory-leak.js +++ b/test/parallel/test-diagnostics-channel-memory-leak.js @@ -1,24 +1,22 @@ -// Flags: --expose-gc +// Flags: --expose-internals --max-old-space-size=16 'use strict'; // This test ensures that diagnostic channel references aren't leaked. -require('../common'); -const { ok } = require('assert'); +const common = require('../common'); -const { subscribe, unsubscribe } = require('diagnostics_channel'); +const { subscribe, unsubscribe, Channel } = require('diagnostics_channel'); +const { checkIfCollectableByCounting } = require('../common/gc'); function noop() {} -const heapUsedBefore = process.memoryUsage().heapUsed; - -for (let i = 0; i < 1000; i++) { - subscribe(String(i), noop); - unsubscribe(String(i), noop); -} - -global.gc(); - -const heapUsedAfter = process.memoryUsage().heapUsed; - -ok(heapUsedBefore >= heapUsedAfter); +const outer = 64; +const inner = 256; +checkIfCollectableByCounting((i) => { + for (let j = 0; j < inner; j++) { + const key = String(i * inner + j); + subscribe(key, noop); + unsubscribe(key, noop); + } + return inner; +}, Channel, outer).then(common.mustCall());