Skip to content

Keep snapshot images shallow so serializing and restoring them cannot overflow the stack - #59

Merged
Project516 merged 7 commits into
masterfrom
p516/warm-restore-depth
Nov 6, 2026
Merged

Project516 merged 7 commits into
masterfrom
p516/warm-restore-depth

Conversation

@Project516

@Project516 Project516 commented Nov 6, 2026 •

Copy link
Copy Markdown
Owner

A warm snapshot restore no longer overflows the stack. Before, the restore sometimes failed and the VM silently booted cold, losing the warm start.

v8.deserialize, v8.serialize and the browser's structuredClone recurse once per level of nesting. A snapshot image was one tree of records: each Java object's field list held the records of the objects it referred to, so a chain of N linked objects nested about 2N levels. A javac library VM's warm image nested 1668 levels. Whether that overflowed depended on how much stack the caller had left, so it happened intermittently, mostly for the second VM in a process. With a long linked list in the heap, master fails at the save too.

  • Each object is written in place once, inside the first record that refers to it, down to 64 levels. A deeper object, and any later reference to an object with contents, goes through an index into a table on the image. Objects with nothing inside them (strings, classes, methods, fields, threads, objects with only primitive fields) stay shared directly, since they add one level at most.
  • The decoder fills objects from a work list, as the encoder already did. It fills the image's lists in place, which is safe because each restore gets its own copy (a fresh deserialize under Node, structuredClone in the browser worker).
  • Images carry format: 2. An image of another format is ignored, so the VM boots and saves a new one.
  • test/warm-snapshot.test.ts sometimes halted the VM before the idle save ran. It now waits for the warm snapshot saved|busy|restored event.

Nesting, before and after: warm image 1668 to 130 levels, boot snapshot 141 to 72.

Size and time, alternating runs:

master this branch
warm image 17.95 to 18.01 MB 18.72 MB (+4%)
boot snapshot 1.06 MB 1.28 MB (+20%)
warm restore, VM start, median of 12 260 ms 231 ms
boot restore, VM start 75 to 90 ms 62 to 76 ms

The boot snapshot grows by 0.2 MB because the index pairs take more room than V8's own back-references. Restores are not slower.

tryEnc (from #58) now also undoes the index table and the shared-object set when a method's constants cannot be saved.

Tests:

  • test/snapshot-depth.test.ts builds a 200,000-node doubly linked list in Java, saves a warm snapshot and restores it in a second VM in the same process. It checks that the restore happened, the list is intact, and the image nests less than 400 levels; it also checks the boot snapshot's nesting. On master it fails at the save.
  • test/warm-snapshot.test.ts passed 10 runs in a row.
  • pnpm typecheck and pnpm test pass.

@coderabbitai

coderabbitai Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 10 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 968e77a1-008e-49b9-8638-3525b10f7bd2
Commits

Reviewing files that changed from the base of the PR and between 359f44d and f3f4e64.

Files selected for processing: 5

  • src/jvm.ts
  • src/snapshot.ts
  • test/snapshot-depth.test.ts
  • test/snapshot-depth/Chain.java
  • test/warm-snapshot.test.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

…epth

# Conflicts:
#	docs/browser.md
#	docs/node.md
#	src/jvm.ts
#	src/snapshot.ts
#	test/warm-jit.test.ts

@project516-review-bot project516-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR introduces snapshot format 2 with a shallow encoding strategy: objects are inlined only to 64 levels of nesting, with deeper objects and repeated references stored in a flat table. The encoder uses a work-list and tracks leaf objects (strings, classes, threads, objects with only primitive fields) that are always shared directly. The decoder fills objects iteratively from the work list. A format check in jvm.ts rejects older images. New test snapshot-depth.test.ts validates a 200,000-node linked list restores correctly and that nesting stays under 400 levels. The warm-snapshot test now waits for the idle save event to avoid a race.


review-bot, model nvidia/nemotron-3-ultra-550b-a55b:free, verdict approve

@Project516
Project516 merged commit ef567c4 into master Nov 6, 2026
16 checks passed
@Project516
Project516 deleted the p516/warm-restore-depth branch November 6, 2026 15:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant