diff --git a/bindings/profilers/heap.cc b/bindings/profilers/heap.cc index 3b87fad1..1c467445 100644 --- a/bindings/profilers/heap.cc +++ b/bindings/profilers/heap.cc @@ -89,7 +89,6 @@ NAN_METHOD(HeapProfiler::StartSamplingHeapProfiler) { #if NODE_MAJOR_VERSION >= 26 if (allocations) { flags = static_cast( - v8::HeapProfiler::kSamplingForceGC | v8::HeapProfiler::kSamplingIncludeObjectsCollectedByMajorGC | v8::HeapProfiler::kSamplingIncludeObjectsCollectedByMinorGC); } diff --git a/ts/src/heap-profiler.ts b/ts/src/heap-profiler.ts index e520d45e..502340aa 100644 --- a/ts/src/heap-profiler.ts +++ b/ts/src/heap-profiler.ts @@ -204,9 +204,8 @@ export function profileV2( * @param intervalBytes - average number of bytes between samples. * @param stackDepth - maximum stack depth for samples collected. * @param allocations - include total allocation samples in the profile. - * When true, each call to `profile()` forces a full GC (via - * v8::HeapProfiler::kSamplingForceGC) to classify sampled objects as - * live/dead, which adds a measurable pause. Requires Node.js >= 26. + * Retains samples of objects collected by GC. Liveness reflects the most + * recent GC; collecting a profile does not force one. Requires Node.js >= 26. */ export function start( intervalBytes: number, diff --git a/ts/test/test-heap-profiler.ts b/ts/test/test-heap-profiler.ts index 26bcca70..3bfd772c 100644 --- a/ts/test/test-heap-profiler.ts +++ b/ts/test/test-heap-profiler.ts @@ -23,7 +23,7 @@ import { AllocationProfileNodeWithStats, LabelSet, } from '../src/v8-types'; -import {fork} from 'child_process'; +import {execFileSync, fork} from 'child_process'; import path from 'path'; import fs from 'fs'; @@ -330,6 +330,31 @@ describe('HeapProfiler', () => { }); }); +it('should collect allocation profiles without forcing GC', function () { + this.timeout(30000); + if (Number(process.versions.node.split('.')[0]) < 26) this.skip(); + + const output = execFileSync( + process.execPath, + [ + '--trace-gc', + '-e', + ` + const heap = require(${JSON.stringify(require.resolve('../src/heap-profiler'))}); + heap.start(512 * 1024, 64, true); + global.retained = Array.from({length: 200000}, (_, i) => ({i})); + console.time('10 allocation profiles'); + for (let i = 0; i < 10; i++) heap.profile(); + console.timeEnd('10 allocation profiles'); + heap.stop(); + `, + ], + {encoding: 'utf8', timeout: 25000}, + ); + console.log(output.trim()); + assert.doesNotMatch(output, /sampling profiler/); +}); + describe('foreign heap sampler', () => { // Regression test: V8's sampling heap profiler can be enabled by something // other than pprof (inspector, DevTools, another agent). getAllocationProfile