For AI agents: the complete documentation index is available at https://rspack.rs/llms.txt, the full documentation bundle is available at https://rspack.rs/llms-full.txt, and this page is available as Markdown at https://rspack.rs/guide/diagnostics/memory.md.
close

Memory

Rspack supports capturing memory snapshots during builds and HMR updates. When you report a memory issue, include these files to help maintainers investigate.

For users

Enable profiling

Use a debug or profiling package at this version or later (standard release packages do not include allocative support). Snapshot collection is off by default. To enable it, set RSPACK_ALLOCATIVE_DIR to an output directory when running your project.

Direct Rspack users

If your project depends on @rspack/core directly, replace it with the debug package at the same version. For example, change 2.3.0 to npm:@rspack-debug/[email protected]:

package.json
 {
   "devDependencies": {
-    "@rspack/core": "2.3.0"
+    "@rspack/core": "npm:@rspack-debug/[email protected]"
   }
 }

Keep the dependency in its original section (dependencies or devDependencies), then run pnpm install.

Users of frameworks such as Rsbuild

If your project uses Rspack indirectly through a framework such as Rsbuild, add a package manager override for @rspack/core. For pnpm, add this configuration to package.json:

package.json
{
  "pnpm": {
    "overrides": {
      "@rspack/core": "npm:@rspack-debug/[email protected]"
    },
    "peerDependencyRules": {
      "allowAny": ["@rspack/*"]
    }
  }
}

Use the same version as the framework's resolved @rspack/core dependency (2.3.0 in this example), then run pnpm install. See Debugging for more about diagnostic package overrides.

For either approach, you can use a profiling build instead of the debug package. Set RSPACK_ALLOCATIVE_DIR when running your project's original build or dev command. Leave it unset to skip snapshot collection. If no suitable package is available, see Build a local binding to build from source.

Capture memory snapshots

Capture a build

Run the project's original build command with an absolute output directory:

RSPACK_ALLOCATIVE_DIR=/absolute/path/profiles/build-1 pnpm exec rspack build

Verify that the build, its configuration, and its plugins all load the intended diagnostic package. For Rspack-integrated frameworks, keep the framework's normal build command and use a compatible diagnostic package override.

After the initial build completes, the output directory contains:

build-1/
  0-build.allocative
  0-build.counts.json
  0-build.metadata.json
  0-build.warnings.txt

Use a fresh directory for each process. Snapshot numbering is shared across compilers within a process and resets in a new process. Reusing a directory between processes can overwrite files.

Capture HMR updates

Set the same output variable when starting the dev server:

RSPACK_ALLOCATIVE_DIR=/absolute/path/profiles/hmr-1 pnpm exec rspack dev

Edit a source file and wait for each rebuild to finish. A single compiler with three rebuilds produces four byte snapshots:

0-build.allocative
1-rebuild.allocative
2-rebuild.allocative
3-rebuild.allocative

Each snapshot also has matching counts, metadata, and warnings files. Watch-mode builds using the same package can also collect snapshots via RSPACK_ALLOCATIVE_DIR.

Keep the project's build mode and sourcemap settings while reproducing the issue, and record the edits made for each HMR update.

Attach data to an issue report

After the build or the intended HMR updates have completed, compress the entire output directory and attach the archive to your issue report. Include all four file types for every snapshot:

FileContents
*.allocativeThe Rust data-structure memory snapshot.
*.counts.jsonModule and dependency entry counts.
*.metadata.jsonSnapshot identity, memory totals, and collection metadata.
*.warnings.txtCoverage and accounting warnings; include it even if empty.

For example, archive the build capture directory:

tar -czf rspack-memory-build-1.tar.gz -C /absolute/path/profiles build-1

For an HMR issue, archive the complete sequence from the initial build through the last relevant rebuild, including all matching files for each snapshot. Keep the original filenames so maintainers can match the snapshots and sidecars.

Include this context in the issue report:

  • The Rspack and diagnostic package versions, Node.js version, and operating system.
  • The command, configuration, build mode, and sourcemap setting used for the capture.
  • A reproduction project or reproduction steps; for HMR, include the edits and their order.
  • The observed memory issue, when it occurs, and the build logs. If no snapshots were produced, report that along with the command and logs.

The developer section explains how to inspect the data.

For Rspack developers

Use the allocative skill

The Rspack repository includes the rspack-allocative skill. The skill prepares build artifacts, runs the workload, and generates memory snapshots and count reports.

Default benchmark projects

The skill uses the repository's built-in benchmark projects by default and prepares the fixtures as needed. With no project or test case specified, it captures the Three.js 10x development build:

$rspack-allocative capture memory and module/dependency entry counts

You can also name a preset workload, such as threejs-10x-production-sourcemap for a production build with sourcemaps.

External projects

To capture an external project, specify its directory and build mode:

$rspack-allocative --project-dir /path/to/my-app production build with sourcemap; collect memory and module/dependency entry counts

--project-dir is an argument in the skill request, interpreted by the agent — it is not a Rspack CLI option. Relative paths resolve from the agent's initial working directory; quote paths that contain spaces. You can also describe the project directory in natural language. The project directory selects the workload; the Rspack checkout selects the local binding and JavaScript packages. If a project has several configurations, specify the configuration file and build mode in the request.

Rspack test cases

The skill also supports collecting data directly from Rspack test cases. Specify the case directory, for example:

$rspack-allocative capture memory for test case hotCases/source-map/disable; run all HMR updates with sourcemaps disabled; keep every memory snapshot and module/dependency entry count report

For example, using the hotCases/source-map/disable case produces these memory snapshot files:

0-build.allocative
1-rebuild.allocative
2-rebuild.allocative
3-rebuild.allocative

Each snapshot also has matching .counts.json, .metadata.json, and .warnings.txt files.

You can use a separate output directory for each run to avoid overwriting data. The skill reports the selected project, build method, capture phase, output paths, and coverage limits. Inspect the snapshots in Speedscope as described in View snapshots.

View snapshots

A snapshot traces ownership paths from the compiler and registered global roots. Its weights are bytes, not execution time or allocation events.

Open Speedscope and drag in a .allocative file. Use search and zoom to find a data structure, and select Left Heavy to explore the largest ownership paths.

Wider frames represent more bytes. The paths describe ownership, not allocation call stacks. The folded format does not carry a unit, so treat the weights as bytes even if the viewer displays a different label. Time Order is not a timeline for these snapshots.

Read the matching .metadata.json and .warnings.txt before interpreting the total. Metadata includes the represented byte total, shared-ownership policy, and global string arena usage. Warnings identify inaccessible nested allocations and other coverage gaps.

Read entry counts

The .counts.json sidecar records module and dependency entries in the current compilation's module graph, grouped by type. These are collection cardinalities — not a process-wide object count or allocation count. An empty graph produces explicit zero-valued module and dependency populations.

Do not derive counts by dividing byte weights by a type's size or by counting lines in a .allocative file. A collection can reserve more storage than its length, and multiple references can share one allocation.

The skill automatically prepares summary.json, counts.csv, and counts.folded when count data is available. Open a nonempty counts.folded file in Speedscope to explore entry counts rather than bytes. Zero-valued populations remain in the table and summary but produce no chart frames.

Interpret the results

Compare matching snapshots from the same phase and with the same workload. For example, if module and dependency counts stay stable during repeated HMR updates while represented bytes grow, inspect which ownership paths retain more data. This observation alone does not establish a memory leak.

Limitation:

  • Snapshots do not measure RSS, peak memory, or cumulative allocations.
  • JavaScript and V8 heap objects, callback captures, some external state, allocator metadata, and private container overhead are not fully represented. Nonblocking locks and unsupported nested types can also limit coverage.

Advanced

How memory collection works

Rspack maintains a fork of Buck2's allocative in crates/rspack_allocative. The fork uses Rust's compile-time reflection and a compiler driver to collect memory data automatically in most cases, reducing the need for manual Allocative implementations.

Borrowed references do not imply ownership; raw pointers and unsupported values are treated as opaque. Shared allocations are counted once under the first encountered owner. These policies avoid double-counting shared payloads, but inaccessible state still limits coverage.

When manual Allocative is needed. Ordinary structs and most enums are instrumented automatically, but some cases require explicit adapters or annotations:

  • Third-party types require an Allocative implementation in rspack_allocative.
  • Unions aren't supported by the derive macro — implement Allocative by hand.
  • Custom ownership where memory layout doesn't match actual ownership, such as arena-allocated data, borrowed buffers, or shared pointers. Write a manual implementation to express the right traversal.
  • Opaque or unsupported values like raw pointers, callback captures, and V8 heap objects. Mark these fields with #[allocative(skip)] to exclude them.
  • Incorrect generic bounds from the default derive. Use #[allocative(bound = "...")] to supply your own constraints.
  • Enum reflection edge cases. The compiler driver's enum reflection needs Self: 'static and skips enums with lifetime parameters, const generics, or non-literal array types. These fall back to typed traversal and need explicit adapters.

The capture flow is:

Binding build
  Include the allocative feature, cfg, and compiler driver
       ↓
Build or rebuild completes
  Finish cache processing; check RSPACK_ALLOCATIVE_DIR
       ↓
Root traversal
  Compiler → Compilation and other owned fields
  Registered globals + process-global Ustr string arena
       ↓
Ownership traversal
  Custom Allocative::visit or reflected fields
  Deduplicate shared payloads; record coverage warnings
       ↓
Snapshot output
  Byte profile + entry counts + metadata + warnings

The capture points are in compiler/mod.rs and compiler/rebuild.rs. snapshot_allocative visits the roots and writes the files. This measures the represented memory at that point, with the limits described in Interpret the results.

Build a local binding

After setting up the development prerequisites, run these commands from the Rspack repository root:

pnpm install
pnpm run build:binding:debug
pnpm run build:js

The debug build includes allocative support by default; snapshot collection is still off by default. The build:binding:debug command uses the release-debug Cargo profile. Standard release builds do not include allocative support. The profiling build also includes this support by default:

pnpm run build:binding:profiling

To include allocative support in the development build (build:binding:dev) or another binding build profile, set ALLOCATIVE=1 when building:

ALLOCATIVE=1 pnpm run build:binding:dev
Environment variableWhen to set itPurpose
ALLOCATIVE=1Building a binding that does not include it by defaultInclude allocative instrumentation in the binding.
RSPACK_ALLOCATIVE_DIRRunning a local build or testEnable snapshot collection and select an output directory.

Setting ALLOCATIVE=1 at runtime alone does not add instrumentation to an existing binding. Debug and profiling builds do not collect snapshots unless RSPACK_ALLOCATIVE_DIR is set.

Profile a local build or test

Run the built CLI from the target project's working directory:

RSPACK_ALLOCATIVE_DIR=/absolute/path/profiles/local-build-1 \
  node /path/to/rspack/packages/rspack-cli/bin/rspack.js build \
  --config ./rspack.config.mjs

Tests using the local binding can also collect snapshots via RSPACK_ALLOCATIVE_DIR. Each build or rebuild produces a snapshot automatically; the test framework does not need a separate capture integration. After building the binding and JavaScript packages, run a focused HMR case:

cd /path/to/rspack/tests/rspack-test
RSPACK_ALLOCATIVE_DIR=/absolute/path/profiles/hmr-test-1 \
  pnpm test "\"$PWD/HotNode.test.mjs\"" --project base \
  -t hotCases/source-map/disable --maxWorkers 1

The literal double quotes in the positional argument select the exact HotNode.test.mjs runner; -t selects the fixture. Use one worker and a fresh output directory. For other runners or concurrent processes, give each process its own directory to avoid overwriting snapshots. See Testing for the repository's test conventions.

Prepare count reports

For manually captured snapshots, run the report helper with a new output directory:

pnpm --dir /path/to/rspack exec zx \
  /path/to/rspack/.agents/skills/rspack-allocative/scripts/render-profile.mjs \
  /absolute/path/profiles/build-1/0-build.allocative \
  --out-dir /absolute/path/profiles/build-1/report-0

It discovers matching sidecars and generates the summary and count reports described in Read entry counts. The skill runs this step automatically.