A non-sucking garbage collector

"Mark Hahn" <[email protected]> Sat, 17 Jul 2004 11:00:01 -0700
Newsgroups gmane.comp.lang.prothon.user
Message-ID <[email protected]>
I have figured out how to have my cake and eat it too. I have a design for
the world's only garbage collector that has all the desired features with no
trade-offs:

1) Mark and sweep (tracing) for tunable maximum performance including
multiple cpus.

2) Continuous operation.  Interpreter never stops (except for thread
switching).

3) Deterministic finalization can be added based on scope.

This isn't a wild-idea based on some untried technology either.  The idea is
to use the same tried and true technique used by file servers for backup
while still serving files.

---- How it works ----

When it is time for the garbage collector to do the mark pass, it will set a
global that "freezes" all objects.  The object locking code will then make
all existing objects read-only.  When a write-lock request comes in for an
object, a copy of the object is made, let's call it a write-proxy, and a
pointer in the original object will point to the proxy.  The changes will
then be made to the proxy.  New objects will be created as usual but they
will be in the same class as the write-proxies.

While in this freeze mode the original set of objects will exist in their
frozen unchanged state exactly as they were at the moment they were
flash-frozen.  A new set of objects will pile up of changed and new objects.
Since the "working set" of new and changed objects tends to be small and
localized, there will be a surge of performance hit when the freeze first
happens but pretty quickly the overhead will be reasonable.

Meanwhile the garbage collector thread will be going through the frozen set
of objects marking them.  It will have the luxury of not using any locking
code at all since the objects are all read-only.  When it is done it will
turn off the freeze mode.  Of course the goal will be to keep the duration
of the freeze mode as short as possible.

When freeze mode is turned off, one might think you should go through and
copy all the proxy objects back, but you really don't have to right away.
Some of the proxy objects are already unreachable and should be garbage
collected.  So you leave them there and let read accesses use them through
the indirect pointer just as they were used in freeze mode.  When they
happen to get write locked for any reason then you take that opportunity to
copy them back which turns out to be cheaper than the original copy because
their are no mallocs.  The garbage collector will pick up dead ones later
when it finds the dead original matching object and deletes the pair
together.  If memory gets low you can add a pass to the garbage collector to
copy them all back to reclaim memory.

---- Cost ----

The cost of this scheme appears in two ways.  One is that every object
reference now must be a handle (double link) instead of a simple pointer
(single link).  This was going to be needed to add advanced generational
garbage collector techniques anyway.  The second is the already mentioned
copy overhead when in freeze mode.

---- Deterministic finalization ---- 

The deterministic finalization can be added by a seperate mechanism Paul
suggested.  A seperate frame-based object collector mechanism is used in
tandem with the one above that works in a Prothon frame scope only. It
reclaims objects immediately that were created in a frame and never left the
frame before the frame was destroyed.  This is analogous to local vars on a
stack in C.  I will change the wiki item that is in the 1.0 list for the
future to specifically describe this feature.