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:

  1. 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.
  2. Marking: Starting from the roots, Oilpan traverses the object graph by calling the Trace method 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.
  3. 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.
  4. 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.