Blink‎ > ‎

Garbage Collection for Blink C++ objects (a.k.a. Oilpan)


Introduction

Terminology

  • Heap
    In Oilpan context, "heap" means memory to store objects which support garbage collection.

How to write code with GC objects

Basic rules

If you'd like to make a class T garbage-collected, you should do
  1. Inherit GarbageCollected<T> or GarbageCollectedFinalized<T>
  2. Make garbage-collected data members Member<U>
  3. Add T::trace(Visitor*), and trace the garbage-collected data members
  4. If T is a data member of another garbage-collected class U, use Member<T> and trace it in U::trace(Visitor*)
  5. Use Persistent<T> if T is a data member of a non-garbage-collected class
  6. Use T* if it is a local variable or a function argument.

1. GarbageCollected<T> or GarbageCollectedFinalized<T>
If we need to call the destructor of the class T, we need to apply GarbageCollectedFinalized<T>. The destructor won't be called for GarbageCollected<T>.
Also, we need to put GarbageCollected<T> or GarbageCollectedFinalized<T> at the first in the base class list.

Destructor

Destructors are called for GarbageCollectedFinalized. However we can't access other garbage-collected objects in destructors. We can't manage destruction order of objects, and we can't know when collectable objects are destructed.
We recommend to make destructor implementations empty.

Part classes

If a class P is always used as a data member of another class O, P can't be garbage-collected. If P has a data member of a garbage-collected class, we should do the followings:
  • Make the data member Persistent<> if O is not garbage-collected
  • If O is garbage-collected,
    • Make the data member Member<>
    • Mark P DISALLOW_ALLOCATION()
    • Add P::trace()
    • Trace P in O::trace()
class P { DISALLOW_ALLOCATION(); public: void trace(Visitor* visitor) { visitor->trace(m_data); } private: Member<T> m_data; }; class O : public GarbageCollected<O> { public: void trace(Visitor* visitor) { visitor->trace(m_p); } private: P m_p; };

Stack-only classes

If a class S is always allocated on a stack, and S has a data member of a garbage-collected class, we should do
  • Mark S STACK_ALLOCATED()
  • Make the data member Member<>
  • Not add S::trace()

WeakMember<>

It becomes nullptr when garbage collection is executed and the pointed object can be collectable.

HeapHashSet<WeakMember<T>> and HeapHashMap<WeakMember<T>, U> have special behavior. When a T object stored in them is collected, HashSet/HashMap entry for it is automatically removed.

Multiple inheritance of garbage-collected classes, and USING_GARBAGE_COLLECTED_MIXIN(type)

A class can't inherit multiple classes which inherit GarbageCollected or GarbageCollectedFinalized. In such case, one of the base classes should inherit GarbageCollected or GarbageCollectedFinalized, and other base classes should inherit GarbageCollectedMixin. The derived class should have USING_GARBAGE_COLLECTED_MIXIN(DerivedClass).

class Base1 : public GarbageCollected<Base1> { virtual void trace(Visitor*); }; class Interface : public GarbageCollectedMixin { virtual void trace(Visitor*); }; class DerivedClass : public Base1, public Interface { USING_GARBAGE_COLLECTED_MIXIN(DerivedClass); public: virtual void trace(Visitor* visitor) OVERRIDE { Base1::trace(visitor); Interface::trace(visitor); } };

Collections, and ALLOW_ONLY_INLINE_ALLOCATION()

Collections of references to garbage-collected class T should be
  • "Heap" version of collection classes.  e.g. HeapVector, HeapHashSet, HeapHashMap
  • Component type should be Member<T> or WeakMember<T>.  Do not use T*.

Stack-allocated heap collections are automatically traced.  Data members of heap collections needs to be traced in trace(Visitor*).

Collections of class T which contains references to garbage-collected classes should be
  • Normal collection of T, and T contains Persistent<>.
It works.  But we'd like to avoid Persistent<>. We recommend:
  • "Heap" version of collection classes
  • Component type should be T as is.
  • T should have ALLOW_ONLY_INLINE_ALLOCATION(), Member<>s, and trace() function.
ALLOW_ONLY_INLINE_ALLOCATION() means the class can't be used outside of collections.

// Not recommended: class T { private: Persistent<G> m_t; }; Vector<T> list; // Recommended: class Component { ALLOW_ONLY_INLINE_ALLOCATION(); public: void trace(Visitor* visitor) { visitor->trace(m_g); } private: Member<G> m_g; }; HeapVector<T> list;

Threading

A garbage-collected object is owned by a thread which allocated it, and the object is destructed in the owner thread.  Objects in other threads can have references to the object.  All of references to the object must be cleared before the owner thread termination, and the object can't outlive the owner thread.

A Persistent<T> object is also owned by a thread which allocate it. It must be destructed in the owner thread.  We can destruct CrossThreadPersistent<T> in non-owner thread.  However it doesn't mean we can transfer the ownership of an object pointed by a CrossThreadPersistent<T> to another thread.

Transition to GC from reference counting

Transition table

Old New Transition  
class Foo : public ScriptWrappable, public RefCounted<Foo> { class Foo : public GarbageCollectedFinalized<Foo>, public ScriptWrappable {
RefCountedWillBeGarbageCollectedFinalized If we need to run the destructor of one of the parent classes, we should  make this class "Finalized."
We should put GarbageCollected(Finalized) first if the class uses multiple inheritance.
class Foo : public RefCounted<Foo> {
public:
    ~Foo() { /* meaningful code here */ }

class Foo : public GarbageCollectedFinalized<Foo> {
public:
    ~Foo() { ... }
RefCountedWillBeGarbageCollectedFinalized If we need to run the destructor of this class, we should make this class "Finalized."
class Foo : public RefCounted<Foo> {
public:
    ~Foo() { }
    OwnPtr<Bar> m_bar;

class Foo : public GarbageCollectedFinalized<Foo> {
public:
    ~Foo() { }
    OwnPtr<Bar> m_bar;
RefCountedWillBeGarbageCollectedFinalized If we need to run destructors of data members, we should make this class "Finalized."
class Foo : public RefCounted<Foo> { class Foo : public GarbageCollected<Foo> {
RefCountedWillBeGarbageCollected If we don't need to run the destructor at all, we should make this class "GarbageCollected."
class Foo : public RefCounted<Foo> { ... };
class Bar : public Foo { ... };
class Foo : public GarbageCollected<Foo> { ... };
class Bar : public Foo { ... };
  We don't need to make derived classes GarbageCollected.

Old New Transition  
class Foo {
    RefPtr<Bar> m_bar;
};
class Bar : RefCounted<Bar> { ... };
class Foo {
    Persistent<Bar> m_bar;
};
class Bar : GarbageCollected<Bar> { ... };

RefPtrWillBePersistent A data member pointing an on-heap object from off-heap object should be Persistent.
class Foo : RefCounted<Foo> {
    RefPtr<Bar> m_bar;
};
class Bar : RefCounted<Bar> { ... };
class Foo : GarbageCollectedFinalized<Foo> {
    RefPtr<Bar> m_bar;
};
class Bar : RefCounted<Bar> { ... };
  No changes for data members pointing off-heap objects from on-heap objects.
class Foo : RefCounted<Foo> {
    RefPtr<Bar> m_bar;
};
class Bar : RefCounted<Bar> { ... };
class Foo : GarbageCollected<Foo> {
    Member<Bar> m_bar;
    void trace(Visitor* visitor) { visitor->trace(m_bar); }
};
class Bar : GarbageCollected<Bar> { ... };
RefPtrWillBeMember A data member pointing an on-heap object from another on-heap object should be a Member.
Also, the data member should be traced.
class Foo {
    Vector<RefPtr<Bar>> m_bar;
};
class Bar : RefCounted<Bar> { ... };
class Foo {
    PersistentHeapVector<Member<Bar> > m_bar;
};
class Bar : GarbageCollected<Bar> { ... };
 WillBePersistentHeapVector< RefPtrWillBeMember<>> Collections of on-heap objects must be either of HeapFoo or PersistentHeapFoo. PersistentHeapVector should be used in this case because the field is a member of an off-heap object.
class Foo : RefCounted<Foo> {
    HashSet<RefPtr<Bar> > m_bar;
    HashSet<Baz> m_baz;
};
class Bar : RefCounted<Bar> { ... };
class Foo : GarbageCollected<Foo> {
    HeapHashSet<Member<Bar> > m_bar;
    HashSet<Baz> m_baz;
    void trace(Visitor* visitor) { visitor->trace(m_bar); }
};
class Bar : GarbageCollected<Bar> { ... };
WillBeHeapHashSet< RefPtrWillBeMember<>> Collections of on-heap objects must be either of HeapFoo or PersistentHeapFoo. HeapHashSet should be used in this case because the field is a member of an on-heap object.

Old New Transition  
void f()
{
    RefPtr<Foo> protector(this);
    ....
void f()
{
    ....
RefPtrWillBeRawPtr Local pointers are automatically traced.
PassRefPtr<Foo> create()
{
    return adoptRef(new Foo);
}

Foo* create() {
    return new Foo;
}

RefPtrWillBeRawPtr, adoptRefWillBeNoop Return values and arguments of functions are automatically traced.

void f()
{
    Vector<RefPtr<Foo> > fooList;

void f()
{
    HeapVector<Member<Foo> > fooList;

WillBeHeapVector< RefPtrWillBeMember<>> Collections of on-heap objects must be either of HeapFoo or PersistentHeapFoo. HeapVector should be used in this case because local variables are never persistent.

Comments