How Chromium Shares Read-Only V8 Builtins Across Processes
This article explains the "Embedded Builtins" mechanism used by the Chromium web browser to reduce memory consumption across tabs. By moving V8 JavaScript engine builtins out of the dynamic memory heap and embedding them directly into the browser binary's read-only text section, the operating system can share a single physical copy of this executable code across multiple renderer processes.
What Are V8 Builtins?
V8 builtins are pre-compiled chunks of executable code that the V8
JavaScript engine requires to function. They include implementations of
standard JavaScript objects (such as Array.prototype.sort
or RegExp), internal helper functions, and the bytecode
handlers that power the Ignition interpreter. Every V8 instance, known
as an Isolate, needs access to these builtins to execute JavaScript.
The Historical Memory Problem
Chromium uses a multi-process architecture where different tabs and sites run in separate renderer processes, each containing its own V8 Isolate.
Historically, V8 stored builtins inside a startup snapshot. When a new renderer process started, V8 deserialized this snapshot directly into the Isolate's private, mutable heap memory. Because the deserialization process patched the builtins with memory addresses specific to that individual Isolate, the operating system treated those memory pages as modified and private. Consequently, every renderer process duplicated hundreds of kilobytes of identical builtin code in physical RAM.
The Solution: Embedded Builtins
The mechanism that allows Chromium to share these builtins across processes is called Embedded Builtins (or off-heap builtins). To achieve memory sharing, V8 engineers extracted the builtins from the mutable V8 heap and placed them into the immutable, read-only section of the Chromium binary itself.
This mechanism relies on three core technical concepts:
1. Isolate-Independent Code
For executable code to be shared across multiple processes, it cannot contain hardcoded memory addresses that point to a specific process's heap. V8 engineers rewrote the builtin generation pipeline to produce Position-Independent Code (PIC) and Isolate-independent code.
Instead of embedding fixed memory pointers into the code, V8 dedicates a specific CPU register—known as the Root Register—to hold the base address of the currently executing Isolate. When an embedded builtin needs to access Isolate-specific data, it calculates the target address dynamically using fixed offsets relative to the Root Register.
2. Embedding into the
Binary's .text Section
During the Chromium build process, V8 runs a tool called
mksnapshot. This tool generates the machine code for all
builtins. Instead of serializing this code into the standard heap
snapshot, mksnapshot writes the instructions out as a
massive assembly file (embedded.S).
The C++ compiler and linker then compile this assembly file directly
into the Chromium executable (or shared library, such as
chrome.dll on Windows). The builtins end up in the binary's
.text section, which is the standard read-only segment
where compiled C++ functions reside.
3. OS-Level Virtual Memory Sharing
Because the builtins are located in the read-only .text
section of the binary and are completely free of runtime modifications
(relocations), the operating system handles the memory sharing
automatically.
When Chromium spawns multiple renderer processes, the operating
system's virtual memory manager maps the binary into each process's
virtual address space. Since the .text section is marked as
read-only and executable, the OS maps the virtual addresses of every
renderer process to the exact same physical pages in RAM.
Calling an Embedded Builtin
Because the operating system uses Address Space Layout Randomization
(ASLR), the shared .text section might be loaded at
different virtual addresses in different processes.
To allow code inside the V8 heap to call a builtin located off-heap
in the .text section, each V8 Isolate maintains a small,
private "Builtins Constants Table" on its heap. When an Isolate
initializes, it populates this table with the actual virtual addresses
of the embedded builtins for that specific process. V8 code then calls
builtins indirectly by looking up their addresses in this table, keeping
the heavy executable code completely untouched and shared in physical
memory.