[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Garbage Collection
From: |
David Chisnall |
Subject: |
Garbage Collection |
Date: |
Wed, 25 May 2011 12:47:00 +0100 |
Hello all of the GNUstep-flavoured people,
After Austin's question, it seems that there is some interest in garbage
collection, so I thought I'd post a little bit and explain for the benefit of
people who don't read the commit logs what I've been working on recently:
First, some history. In the beginning, Objective-C implemented +new and -free
methods, which wrapped malloc() and free(), and made it impossible to alias
objects without lots of thought about your objects. No one liked it, so NeXT
introduced explicit reference counting with autorelease pools. This worked in
every situation except two: If you had cycles in your object graph, you needed
to be careful that one pointer was not retained (e.g. the reference of a view
to its delegate). If you made a mistake anywhere, then you'd get a crash -
probably some time after your actual bug.
Some time not much later, GNUstep added support for garbage collection using
the Boehm collector. This required some significant changes to the source, and
the legacy of this is a load of #ifdef GS_WITH_GC lines littered about the
source. As far as I know, this was never actually used by anyone beyond some
demos, and didn't work properly with GUI applications[1].
There were also some limitations with the GNUstep approach - aside from needing
programmer effort, which somehow defeats the point of GC. The lack of weak
references meant that some common patterns caused memory leaks. For example,
in the traditional retain/release world, an object registers itself with
NSNotificationCenter, which holds an unretained pointer to it, and then
unregisters itself in -dealloc, when it is no longer referenced by anything
else. In a garbage collected world, NSNotificationCenter has a reference to
the object, so it will never be freed, so can not unregister itself. The
notification center requires a weak reference, so that the object can be freed
when nothing else holds a reference to it and the notification centre can then
clean up.
With OS X 10.5, Apple introduced their own way of doing garbage collection.
This had the compiler insert read and write barriers for weak references, and
write barriers for strong references. This was, of course, completely
incompatible with the GNUstep approach (NIH is strong at Apple).
Over the past few days, I have been working to implement Apple's solution in
clang and the GNUstep runtime. This required about 500 lines of new code in
the runtime, some tweaks in clang (mainly because someone at Apple had decided
that no platforms other than Darwin supported GC, so the front end was
rejecting the -fobjc-gc options, even though they mostly worked). In -base,
there were very few changes outside of NSZone and NSObject. I have made no
changes at all in -gui, and one minor change in -back (turning malloc() into
NSAllocateCollectable() for some structs that contain pointers).
If you are using trunk versions of the runtime, clang, base, and back,
Apple-compatible garbage collection now works well enough for Gorm to run. It
appears to use marginally less memory than in manual retain / release mode
(most likely due to some autoreleased objects being held in the top-level
autorelease pool).
Apple supports two options: -fobjc-gc, which is intended for frameworks that
support both garbage collection and manual reference counting modes, and
-fobjc-gc-only, which does not permit linking against non-GC code. I have some
preliminary code that supports -fobjc-gc, but it's a bit messy and I'm not sure
it's worth bothering with (anyone have an opinion? Is this actually a useful
feature), but -fobjc-gc-only works quite well (well enough for Gorm, anyway -
I've not done extensive testing).
To use this, all you need to do is compile the runtime with boehm_gc=yes and
add -fobic-gc-only to your OBJCFLAGS everywhere else. Please let me know if
you find anything that breaks. Currently there is one thing that I know
doesn't work:
Because -retain messages are omitted in GC mode, blocks will not be copied if
sent a -retain message. Holding a reference to a block requires an explicit
Block_copy(). I'm not sure the best way to address this (or what Apple does,
in fact). I can probably fudge it slightly with a really ugly hack that checks
whether the isa pointer of an assigned object is a block, but that's a bit of a
kludge).
Please test, please use, please let me know how performance is affected,
David
P.S. The only change to the ABI currently is bumping the module version
(causing old runtimes that don't support GC to reject modules that expect it)
and adding a flag to the module indicating the GC mode (none / optional /
required). This is currently experimental code (neither the compiler nor the
runtime has had an official release with GC support enabled), and is subject to
change, so now is a good time to provide any feedback. Since GC-only code is
incompatible with retain/release code, we get to break the ABI completely, so
now is a good time for feature requests for the new ABI...
[1] I've never actually tried - it's entirely possible that it did, but I was
told that it didn't.
-- Sent from my brain
- Garbage Collection,
David Chisnall <=