raw Software
RAW Software Programming Memory Management

Forcing Garbage Collection in Node.js

Robert Eisele

V8 normally decides when garbage collection is useful, and that is the right behavior for ordinary Node.js applications. Manually requesting a collection is rarely a production optimization: it pauses JavaScript execution, may reduce throughput, and does not fix retained references or memory leaks.

Current status: Manual garbage collection is still available in Node.js, but it is usually unnecessary. The interface is an experimental V8 extension and is most useful for controlled benchmarks, memory-leak tests, or batch programs with a known idle phase.

Expose the Garbage Collector

Node.js does not expose the collector by default. Start the process with the V8 flag:

node --expose-gc app.js

This adds gc() to globalThis. Without the flag, the property is normally undefined.

Request a Collection Safely

function forceGarbageCollection() {
  if (typeof globalThis.gc !== "function") {
    return false;
  }

  globalThis.gc();
  return true;
}

if (!forceGarbageCollection()) {
  console.warn("GC is unavailable; start Node.js with --expose-gc");
}

The return value distinguishes an unavailable hook from a completed call. The request is synchronous from the caller's perspective, so it should never run on every request, timer tick, or allocation cycle.

When a Manual Collection Is Useful

Even in these cases, measure the result. A lower heapUsed value does not guarantee lower resident memory, because V8 may retain heap pages for later allocations and Node.js also uses native and external memory.

Measure Before and After

function heapUsedInMiB() {
  return process.memoryUsage().heapUsed / 1024 / 1024;
}

console.log("before:", heapUsedInMiB().toFixed(1), "MiB");

// Remove all application references to the temporary data first.
globalThis.gc?.();

console.log("after:", heapUsedInMiB().toFixed(1), "MiB");

This only measures JavaScript heap usage. For diagnosis, process.memoryUsage() also reports resident-set, external, and array-buffer memory. Node.js additionally provides heap snapshots, heap profiles, --trace-gc, and the node:v8 GC profiler. Those tools usually reveal more than repeatedly forcing collections.

What Manual GC Does Not Solve

Garbage collection only reclaims unreachable objects. If a cache, event listener, timer, closure, or global collection still references an object, calling gc() cannot release it. A steadily growing post-collection baseline therefore points to retained data and should be investigated with snapshots or allocation profiles.

The --expose-gc flag and the exact behavior of the exposed function belong to V8 rather than the stable JavaScript language API. Keep the hook optional, do not make application correctness depend on it, and re-check Node.js release notes before relying on it in long-lived tooling.