Skip to content

Commit 1586c11

Browse files
joyeecheungrichardlau
authored andcommitted
test: add checkIfCollectable to test/common/gc.js
PR-URL: #49671 Backport-PR-URL: #51004 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Michaël Zasso <[email protected]>
1 parent 7552de6 commit 1586c11

File tree

1 file changed

+70
-0
lines changed

1 file changed

+70
-0
lines changed

test/common/gc.js

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
'use strict';
2+
3+
// TODO(joyeecheung): merge ongc.js and gcUntil from common/index.js
4+
// into this.
5+
6+
// This function can be used to check if an object factor leaks or not,
7+
// but it needs to be used with care:
8+
// 1. The test should be set up with an ideally small
9+
// --max-old-space-size or --max-heap-size, which combined with
10+
// the maxCount parameter can reproduce a leak of the objects
11+
// created by fn().
12+
// 2. This works under the assumption that if *none* of the objects
13+
// created by fn() can be garbage-collected, the test would crash due
14+
// to OOM.
15+
// 3. If *any* of the objects created by fn() can be garbage-collected,
16+
// it is considered leak-free. The FinalizationRegistry is used to
17+
// terminate the test early once we detect any of the object is
18+
// garbage-collected to make the test less prone to false positives.
19+
// This may be especially important for memory management relying on
20+
// emphemeron GC which can be inefficient to deal with extremely fast
21+
// heap growth.
22+
// Note that this can still produce false positives. When the test using
23+
// this function still crashes due to OOM, inspect the heap to confirm
24+
// if a leak is present (e.g. using heap snapshots).
25+
// The generateSnapshotAt parameter can be used to specify a count
26+
// interval to create the heap snapshot which may enforce a more thorough GC.
27+
// This can be tried for code paths that require it for the GC to catch up
28+
// with heap growth. However this type of forced GC can be in conflict with
29+
// other logic in V8 such as bytecode aging, and it can slow down the test
30+
// significantly, so it should be used scarcely and only as a last resort.
31+
async function checkIfCollectable(
32+
fn, maxCount = 4096, generateSnapshotAt = Infinity, logEvery = 128) {
33+
let anyFinalized = false;
34+
let count = 0;
35+
36+
const f = new FinalizationRegistry(() => {
37+
anyFinalized = true;
38+
});
39+
40+
async function createObject() {
41+
const obj = await fn();
42+
f.register(obj);
43+
if (count++ < maxCount && !anyFinalized) {
44+
setImmediate(createObject, 1);
45+
}
46+
// This can force a more thorough GC, but can slow the test down
47+
// significantly in a big heap. Use it with care.
48+
if (count % generateSnapshotAt === 0) {
49+
// XXX(joyeecheung): This itself can consume a bit of JS heap memory,
50+
// but the other alternative writeHeapSnapshot can run into disk space
51+
// not enough problems in the CI & be slower depending on file system.
52+
// Just do this for now as long as it works and only invent some
53+
// internal voodoo when we absolutely have no other choice.
54+
require('v8').getHeapSnapshot().pause().read();
55+
console.log(`Generated heap snapshot at ${count}`);
56+
}
57+
if (count % logEvery === 0) {
58+
console.log(`Created ${count} objects`);
59+
}
60+
if (anyFinalized) {
61+
console.log(`Found finalized object at ${count}, stop testing`);
62+
}
63+
}
64+
65+
createObject();
66+
}
67+
68+
module.exports = {
69+
checkIfCollectable,
70+
};

0 commit comments

Comments
 (0)