# MemLab: Automated JavaScript Memory Leak Detection with Heap Snapshot Diffing

> MemLab is an open-source framework from Meta that finds JavaScript memory leaks by driving a browser with Puppeteer, taking three V8 heap snapshots at defined interaction steps, and comparing them to isolate objects that survived a round trip they should not have survived. Beyond leak detection, it provides a CLI for heap analysis, a Node.js API for unit-level memory assertions, and an MCP server for AI coding assistants.

**facebook/memlab** — A framework for finding JavaScript memory leaks and analyzing heap snapshots

- Repository: https://github.com/facebook/memlab
- Website: https://facebook.github.io/memlab/
- Stars: 5,060 · Forks: 148
- Language: TypeScript
- License: MIT
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/facebook-memlab

## What problem MemLab solves and for whom

JavaScript memory leaks are hard to find manually. An application may appear to work correctly while its memory grows with each user interaction. The leaked objects are still reachable by the garbage collector through a reference chain that should have been cleaned up. Without tooling, developers must take heap snapshots in Chrome DevTools, compare them by eye, and work backward through object references to find the source.

MemLab automates this process. It takes the approach of running a defined interaction sequence in Puppeteer, capturing heap snapshots at three points: before the action, after the action, and after reverting the action. Objects that persist after the revert when they should have been garbage-collected are candidates for memory leaks. MemLab then clusters similar leaks together, filters out known false positives, and shows one representative retainer trace per cluster.

The primary audience is web application developers whose single-page applications exhibit memory growth under interaction. The tool is also used by library authors to verify that their libraries do not retain references after use. Node.js and Electron applications can use it through the heap snapshot analysis API, which accepts `.heapsnapshot` files rather than running Puppeteer.

## Installing MemLab and writing a first scenario

The CLI is available on npm:

```bash
npm install -g memlab
```

A scenario file defines three functions that MemLab calls in sequence. The `url` function returns the starting URL. The `action` function describes the user action that should trigger the leak. The `back` function describes how to revert that action. The README shows a complete example for testing Google Maps:

```javascript
// initial page load url: Google Maps
function url() {
  return 'https://www.google.com/maps/@37.386427,-122.0428214,11z';
}

// action where we want to detect memory leaks: click the Hotels button
async function action(page) {
  // puppeteer page API
  await page.click('text/Hotels');
}

// action where we want to go back to the step before: click clear search
async function back(page) {
  // puppeteer page API
  await page.click('[aria-label="Close"]');
}

module.exports = {action, back, url};
```

With the scenario saved to `test-google-maps.js`, the run command is:

```bash
memlab run --scenario test-google-maps.js
```

MemLab interacts with the page, takes the three heap snapshots, and prints the results. The work directory where snapshots are saved can be retrieved with `memlab get-default-work-dir`.

## How the retainer trace works

A retainer trace is the chain of object references from the GC root to a leaked object. MemLab prints the trace to show why the object is still alive in memory after the revert action. The README includes an example output from a test website:

```bash
MemLab found 46 leak(s)
--Similar leaks in this run: 4--
--Retained size of leaked objects: 8.3MB--
[Window] (native) @35847 [8.3MB]
  --20 (element)--->  [InternalNode] (native) @130981728 [8.3MB]
  --8 (element)--->  [InternalNode] (native) @130980288 [8.3MB]
  --1 (element)--->  [EventListener] (native) @131009888 [8.3MB]
  --1 (element)--->  [V8EventListener] (native) @224808192 [8.3MB]
  --1 (element)--->  [eventHandler] (closure) @168079 [8.3MB]
  --context (internal)--->  [<function scope>] (object) @181905 [8.3MB]
  --bigArray (variable)--->  [Array] (object) @182925 [8.3MB]
```

The numbers after @ are object IDs in the heap snapshot. If the application serves non-minified code with readable names, the trace points directly to a variable like `bigArray` that should have been set to null but was not. Breaking the reference chain at any point in the trace allows the leaked object to be garbage-collected.

For applications that serve only minified code, the object IDs can be used in Chrome DevTools to inspect the leaked object manually. The workflow is to load the snapshot from `$(memlab get-default-work-dir)/data/cur` into DevTools and search for the object ID.

## Custom leak detectors and programmatic heap analysis

The built-in leak detectors handle common patterns but cannot catch every application-specific case. A `leakFilter` callback in the scenario file gives full control over which objects are considered leaks:

```javascript
function leakFilter(node, heap) {
  // ... your leak detector logic
  // return true to mark the node as a memory leak
}
```

The `node` argument is the heap object being evaluated. The `heap` argument is the graph representation of the final heap snapshot, documented at the project's API site. This interface allows detectors that look at specific object types, reference patterns, or retained sizes that the built-in detectors do not consider.

For non-browser workflows, MemLab accepts pre-taken `.heapsnapshot` files. This covers Node.js programs, Electron applications, and the Hermes runtime used by React Native. The view-heap command loads a snapshot file for interactive inspection:

```bash
memlab view-heap --snapshot <PATH TO .heapsnapshot FILE>
```

A specific object can be pinpointed with `--node-id @28173` to jump directly to it in the trace view.

## Memory assertions in Node.js and the unbound-object analysis

MemLab's API package allows Node.js programs to take heap snapshots of their own state at runtime. This enables writing unit tests that assert memory behavior, not just functional behavior. A test can check that a cache object has not grown beyond a threshold, or that an event emitter does not retain listeners after a component unmounts.

The `memlab analyze unbound-object` command identifies objects that grew in size across the interaction sequence. This analysis is useful for finding memory that is not technically leaked but still grows unboundedly. The command also accepts a directory of pre-collected snapshot files:

```bash
memlab analyze unbound-object --snapshot-dir <DIR_OF_SNAPSHOT_FILES>
```

Running `memlab analyze` without arguments lists all built-in analyses. Each analysis targets a different pattern of memory growth, from unbound object growth to specific object types that are known to cause retention in certain frameworks.

## The MCP server and MemLens browser debugging tool

The `@memlab/mcp-server` package, listed in the monorepo's workspaces, provides an MCP server for AI coding assistants. This gives tools like Claude Code and Cursor interactive access to MemLab's heap analysis capabilities through natural language. The AI assistant can load heap snapshots, run leak detection, and investigate optimization opportunities without the developer writing custom commands.

MemLens is a browser-based memory debugging tool in the `packages/lens` workspace. The README describes it as enabling visualization of memory leaks and interactive memory debugging in the browser. MemLens is a separate interface from the CLI, targeting developers who prefer a visual investigation tool over command-line trace output.

The monorepo is organized with each capability in its own package: core, e2e, heap-analysis, api, cli, memlab (the npm user-facing package), mcp-server, and lens. The build system requires Node.js 16 or later, as noted in the root package.json error messages.

## Limitations and what MemLab cannot do

MemLab requires that the application under test serves code with readable identifiers. The README states this directly: to get a readable trace, the website must serve non-minified code, or at least minified code with readable variable, function, and property names. In a pure production environment with fully minified code and no source maps surfaced to the runtime, the retainer traces will show opaque names that are difficult to act on.

The Puppeteer-based approach requires writing and maintaining scenario files. For a large application with many interaction paths, this is a significant authoring effort. MemLab does not automatically discover memory leaks by crawling an application. A developer must explicitly define the action and revert steps for each interaction they want to test.

MemLab is a detection and analysis tool, not a prevention tool. It does not hook into the application's build system to warn about patterns that commonly cause leaks. Finding a leak with MemLab still requires a developer to trace back through the retainer chain and fix the code. There is no automatic remediation.

## Conclusion

MemLab is the right choice for teams with JavaScript applications that exhibit memory growth under use and have enough codebase ownership to write Puppeteer-based interaction scripts. It is not the right choice for applications running minified code without readable variable names, because the retainer traces it produces will be unreadable. Before running MemLab in CI, verify that the website under test serves non-minified JavaScript, or at minimum minified code with readable identifiers. For production memory debugging, the `memlab view-heap` command combined with a heap snapshot taken from Chrome DevTools is the fastest path when a full Puppeteer scenario has not been written yet.

## FAQ

### How do I use memlab to find memory leaks in my web application?

Install the CLI with `npm install -g memlab`, create a scenario file with url, action, and back functions using the Puppeteer page API, then run `memlab run --scenario your-scenario.js`. MemLab will interact with the page, take three heap snapshots, and print retainer traces for any leaked objects it finds.

### What is the difference between MemLab's built-in leak detectors and a custom leakFilter?

The built-in detectors cover common leak patterns automatically. A custom leakFilter callback in the scenario file receives each surviving heap object and lets you define your own criteria for what counts as a leak. This is useful when the application has domain-specific objects that the built-in detectors do not recognize as problematic.

### Does MemLab work with Node.js applications and Electron, not only web browsers?

Yes. The heap analysis API accepts .heapsnapshot files directly, which can be taken from Node.js programs, Electron applications, and the Hermes runtime. The `memlab view-heap` command loads any .heapsnapshot file regardless of where it was collected.

## Sources

- [facebook/memlab on GitHub](https://github.com/facebook/memlab)
- [Issues](https://github.com/facebook/memlab/issues)
- [License: MIT](https://github.com/facebook/memlab/blob/main/LICENSE)
- [Project website](https://facebook.github.io/memlab/)
- [README](https://github.com/facebook/memlab/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/facebook-memlab
