Skip to content

V8 in browser and Node

Both Chrome and Node.js run JavaScript on V8. The language semantics and GC model are the same; the host environment differs.

flowchart TB
  subgraph v8 [V8 Engine]
    parser[Parser / Ignition]
    opt[Optimizing compiler]
    heap[Heap + GC]
  end
  subgraph browser [Browser host]
    dom[DOM / Web APIs]
    perf[performance.memory]
    devtools[Chrome DevTools]
  end
  subgraph node [Node host]
    libuv[libuv event loop]
    proc[process.memoryUsage]
    inspect[--inspect / v8 module]
  end
  v8 --> browser
  v8 --> node

These behave similarly in both environments:

  • Global/module-scope caches
  • Closures capturing large data
  • Forgotten timers (setInterval)
  • Unremoved event listeners (EventEmitter in Node)
  • DOM nodes and event listeners
  • window globals
  • requestAnimationFrame callbacks
  • Service Workers / IndexedDB (persistent by design — not leaks, but retention)
  • Active handles (timers, TCP sockets, file descriptors)
  • Event loop keeps process alive while handles exist
  • Module-level singletons (common in servers)
  • Buffers and native addons (show up in external memory)
Runtime API Notes
Browser performance.memory.usedJSHeapSize Chromium only; approximate
Node process.memoryUsage() rss, heapUsed, heapTotal, external
// server.js — module scope lives for process lifetime
const sessions = new Map();
app.use((req, res, next) => {
sessions.set(req.sessionId, req.body); // never deleted
next();
});
const sessions = new Map();
const TTL_MS = 30 * 60 * 1000;
function setSession(id, data) {
sessions.set(id, { data, at: Date.now() });
}
setInterval(() => {
const now = Date.now();
for (const [id, s] of sessions) {
if (now - s.at > TTL_MS) sessions.delete(id);
}
}, 60_000);