Oilpan C++ Garbage Collection in Chromium's Blink
This article provides an overview of how the Oilpan C++ garbage collector manages the lifecycle of Document Object Model (DOM) objects within Chromium's Blink rendering engine. It covers the transition from manual reference counting to tracing garbage collection, explains the specialized smart pointers used for tracking object reachability, details the mark-and-sweep phases of memory reclamation, and highlights the unified heap integration between Blink and the V8 JavaScript engine.
The Shift from Reference Counting
Historically, Blink relied on manual reference counting to manage the lifespan of C++ DOM objects. This approach caused two major issues: memory leaks due to cyclic references and security vulnerabilities caused by use-after-free bugs when objects were deleted prematurely. Oilpan replaces manual memory management with a tracing garbage collector specifically designed for C++. By coupling object lifetime to reachability rather than reference counts, Oilpan eliminates reference cycles and significantly reduces memory-related security flaws.
Allocation and Oilpan Smart Pointers
To have its lifecycle managed by Oilpan, a Blink C++ class must
inherit from the GarbageCollected<T> base class and
be allocated using the MakeGarbageCollected<T>()
factory function. Instead of raw C++ pointers, developers must use
specific Oilpan smart pointers so the collector can understand the
object graph:
Member<T>: Used inside garbage-collected objects to hold strong references to other garbage-collected objects.WeakMember<T>: Holds a weak reference to a garbage-collected object. It does not prevent the target from being destroyed, and Oilpan automatically clears the pointer to null if the target is collected.Persistent<T>: Used by non-garbage-collected objects (or global variables) to keep an Oilpan-managed object alive. These act as root references for the garbage collector.
For performance reasons, variables sitting directly on the C++ execution stack do not require smart pointers. Oilpan uses conservative stack scanning to automatically find raw pointers on the CPU stack during collection.
Object Tracing
Because C++ lacks native runtime reflection, Oilpan cannot
automatically know where Member<T> pointers are
located inside an object. Therefore, every Oilpan-managed class must
implement a Trace(Visitor*) method. Inside this method, the
object explicitly passes all of its Member<T> and
WeakMember<T> fields to the visitor. The Chromium
build system uses a custom Clang compiler plugin to verify at
compile-time that developers have not forgotten to trace any managed
pointers.
The Mark-and-Sweep Lifecycle
Oilpan manages memory reclamation using a mark-and-sweep algorithm broken down into specific phases to avoid freezing the browser's main thread:
- Root Scanning: The garbage collection cycle begins
by identifying all root objects. Roots include all active
Persistent<T>handles and any pointers discovered on the C++ execution stack. - Marking: Starting from the roots, Oilpan traverses
the object graph by calling the
Tracemethod on each reachable object. To keep the browser responsive, marking is done incrementally (interleaved with main-thread tasks) and concurrently (on background threads). If the main thread alters an object's references while background marking is active, a mechanism called a "write barrier" intercepts the change and notifies the collector so no live objects are accidentally missed. - Sweeping: Once the entire graph is traced, any object not marked as reachable is considered dead. During the sweeping phase, Oilpan iterates through the memory heap, calls the standard C++ destructors on the dead objects, and reclaims their memory blocks for future allocations. Like marking, sweeping is largely offloaded to background threads.
- Compaction: Periodically, Oilpan also compacts the
heap by moving surviving objects together to reduce memory
fragmentation, automatically updating all
Member<T>pointers to the new memory addresses.
The V8 Unified Heap
DOM objects in Blink do not exist in isolation; they are constantly interacted with via JavaScript executed by the V8 engine. A JavaScript wrapper object might point to a C++ DOM node, which in turn points to an event listener back in JavaScript.
To prevent uncollectable reference cycles across the C++ and JavaScript boundary, Oilpan is directly integrated into V8, forming a "Unified Heap." V8 and Oilpan coordinate their garbage collection cycles. During the marking phase, V8 can trace references directly into Oilpan's C++ DOM objects, and Oilpan can trace references back into V8's JavaScript objects. This unified visibility ensures that isolated islands of cyclic C++ and JavaScript objects are accurately identified as unreachable and collected together.