Unifying Chromium Garbage Collection with Oilpan and cppgc

This article explores how the Chromium browser unified memory management between its C++ DOM nodes and V8 JavaScript objects by migrating the Oilpan garbage collector into the standalone cppgc library. By integrating cppgc directly into the V8 engine, Chromium resolved complex issues with cross-language reference cycles, eliminated redundant API tracing boundaries, and centralized garbage collection scheduling to improve overall browser performance and memory efficiency.

Chromium’s architecture relies on two main components that constantly interact: the Blink rendering engine (written in C++) and the V8 JavaScript engine. Web applications frequently create scenarios where JavaScript objects wrap C++ DOM nodes, and those C++ DOM nodes hold references back to JavaScript event listeners or callbacks.

Historically, V8 used its own garbage collector for JavaScript objects, while Blink used a separate C++ garbage collector called Oilpan for DOM nodes. Because neither collector had full visibility into the other's memory space, reference cycles spanning both engines easily resulted in memory leaks or premature object destruction.

The Bottleneck of Cross-Component Tracing

Before the creation of cppgc, Chromium attempted to solve cross-language reference cycles using a mechanism called cross-component tracing. When a garbage collection cycle began, V8 and Oilpan had to communicate across a strict API boundary.

If V8 found a reference to a C++ DOM node, it passed that pointer across the boundary to Oilpan. Oilpan would trace its C++ object graph, and if it found a reference back to a JavaScript object, it passed the pointer back to V8. This "ping-pong" communication created massive performance overhead, duplicated code, and complicated concurrent marking.

Migrating Oilpan to the cppgc Library

To eliminate the boundary between Blink and V8, Chromium engineers extracted the core mechanics of Oilpan out of the Blink rendering engine and refactored it into a generic C++ garbage collection library named cppgc. Crucially, this library was moved directly into the V8 codebase.

By embedding cppgc inside V8, the JavaScript engine gained native ownership and understanding of the C++ heap, transforming two separate garbage collectors into a single, unified system.

How cppgc Unified Garbage Collection

Converting Oilpan to cppgc unified garbage collection between C++ DOM nodes and JavaScript objects in four specific ways:

  • Boundary-Free Object Tracing: Because cppgc resides inside V8, the garbage collector no longer needs to cross an external API boundary to trace references between JS and C++. V8's marker can seamlessly traverse from a JavaScript wrapper object directly into a C++ DOM node and back within the same marking loop.
  • Unified Worklists and Concurrent Marking: Both JavaScript objects and C++ DOM nodes now share the same underlying infrastructure for concurrent marking. Background threads managed by V8 process both C++ and JS pointers simultaneously using shared marking worklists, maximizing CPU cache locality and thread utilization.
  • Centralized GC Scheduling: Previously, Blink and V8 had to guess when to trigger garbage collection based on isolated views of memory consumption. With cppgc, V8 has a holistic view of total memory pressure across both the JS heap and the C++ DOM heap. It can schedule garbage collection sweeps at the most optimal times for the entire browser tab.
  • Unified Write Barriers: To track changes to objects while the garbage collector is running concurrently in the background, engines use "write barriers." The cppgc integration allowed V8 to unify the write barrier mechanics, meaning modifications to a C++ DOM node and a JavaScript object are intercepted and recorded using the exact same logic.

The Impact on Chromium

By consolidating Oilpan into V8 via cppgc, Chromium eliminated the synchronization overhead between its rendering and JavaScript engines. The browser now resolves cyclic references between C++ and JavaScript natively, uses fewer CPU cycles during garbage collection pauses, and reclaims memory much faster, leading to a smoother user experience on complex web applications.