为什么在创建对象之前使用 global.gc() 可以防止堆使用量出现负增量?

Why does using global.gc() before creating an object prevent a negative delta in heap usage?

我有这段代码,旨在 运行 在子进程中(通过 fork,具体而言),以便尝试测量内存中对象的大小:

const syncComputationThatResultsInALargeObject = require('whatever');

let initMemory;
let finalMemory;
let obj;
process.on('message', () => {
  // global.gc();
  initMemory = process.memoryUsage().heapUsed;
  obj = syncComputationThatResultsInALargeObject();
  finalMemory = process.memoryUsage().heapUsed;

  process.send({
      memoryCost: finalMemory - initMemory,
  });
});

使用子进程完成此操作的原因是试图防止父进程中存在的变量造成任何污染。

我观察到的是,令人惊讶的是,有时返回的 memoryCost 是负数,这意味着创建 obj 后堆大小变小了。

但是,如果我在节点中使用 --expose-gc 启用手动 GC 调用,并在轮询堆使用情况之前调用 GC,则在创建对象之前,我永远不会得到负值。

任何人都可以回答为什么会发生这种情况吗?我在 Ubuntu 18.04.1 上使用节点 6.14.4,内核 4.15.0-30-generic。谢谢。

编辑:即使我在分配给 finalMemory 之后引用 obj,也会发生这种情况,例如,通过在传递给 process.send 的对象中放置对其字段之一的引用.

经过更多的测试,我明白了为什么会这样。

在我计算函数的特定实例中,我能够传递几个影响结果对象大小的参数。

我最终观察到的是,当对不同的参数集重复计算几次时,即使我调用 global.gc() 在指示的行。

因此,我将观察到的行为归因于这样一个事实,即有时 GC 在计算函数内部以无法控制的方式被调用。更大的对象大小意味着更大的内存使用量,这意味着更频繁(因此可能不可预测)的 GC 调用,这意味着相同参数的结果分散性更大。