Managed memory and shared_ptr<T>s

In my last post I talk about how Boost implements shared_ptr<T>s with an intermediate object between the shared_ptr and the target. The intermediate object serves a few purposes.

It holds the reference count

Otherwise it would have to forgo the reference count (like a true garbage collector), or keep the reference count in a separate map/dictionary, or force the target object to make room for the reference count. The intermediate object lets shared_ptr<T> provide an efficient implementation that works with any target class.

It enables weak pointers

Weak pointers of class weak_ptr<T> point to the same intermediate object as shared_ptr<T>. There are ways to implement weak pointers without the intermediate object, but they are questionable. An intrusive implementation would require the target to provide a pointer from the target object to a linked list of weak pointers, which could then be cleared before the target was deleted. But this involves a lot of mutex-style locking and can be expensive. The Boost implementation is very clever about avoiding these kinds of locks, instead using interlocking increment/decrement.

Other solutions, involving delayed delete and property maps/dictionaries, are even less attractive.

It enables compaction

Some garbage collecting systems always use intermediate objects. The intermediate objects all together are usually called the object table. One of the big advantages is that when the garbage collector moves a target object it only has to update a single pointer, the one in the object table. The object table entries (the intermediate objects) do not move, but fragmentation isn’t much of a problem because they are small, kept in an array, and reused often.

It is easy to see how you might set up something similar in C++. It would provide compaction and reduce memory fragmentation even without full garbage collection. You’d allocate managed objects from a special heap, and when the heap got too fragmented you’d create another heap, copy the objects across, update all the pointers in the object table, and destroy the first heap. This is especially attractive if you are coding in a no-side-effects functional style.

(It wouldn’t work with Boost 1_37 shared_ptr<T>s though, since they not only store the target pointer in the intermediate object (where it is used for delete) but also in the shared_ptr<T> and weak_ptr<T> objects themselves. They do this because casting and aliasing require that the target pointer and the deleted pointer sometimes be different.)

← Previous Page