[Openmcl-devel] Memory managemet Objective C and Clozure

Gary Byers gb at clozure.com
Tue Sep 14 13:23:18 PDT 2010

On Tue, 14 Sep 2010, Willem Rein Oudshoorn wrote:

> I have a few questions about how the memory management of CCL and
> Objective C work together.  Hopefully it is already somewhere fully
> documented, in that case, please point me to the right location
> (A quick google search and reading the manual did not yield much.)
> Let me rephrase the questions into a hypothesis and consequences.
> If someonw can help me confirm or refute this I would appreciate it.
> Hypothesis A:
>           1. The objective C runtime does not know anything about
>              the lisp garbage collector
>           2. The lisp GC and Objective-C bridge do NOT care about
>              the objective C runtime memory managment.
> Consequence/Questions:
>        1. An object created in lisp by:
>           ?  (#/init (#/alloc)))
>           Is a memory leak, as soon as the GC collector collects the
>           lisp 'wrapper' of the object, all references on the lisp side
>           are gone, but #/release is not called on the objective C
>           instance.
>           [Nitpicking, it is possible that the Objective C runtime
>           holds one copy of an NS-Object and reuses it.  In that case
>           of course it does not leak.  But for argument sake assume the
>           objective C runtime creates a fresh copy of the object
>           everytime.]

Yes.  Your example isn't too far removed from the case where one does

(#_malloc 10)

and loses track of the lisp pointer (and never calls #_free).

One doesn't typically do that sort of thing in a simple cases (e.g., where
the lifetime of the pointer is well-defined); in more complicated cases,
it's all too easy to lose track of foreign pointers and it's hard in some
cases to know when they can be safely freed.  (Manual memory management is
hard, and that isn't news; if you're used to having a GC take care of this
sort of thing for you, the world of #_malloc/#_free is likely to seem shockingly
primitive.  And it is.  Someone should tell people that write OSes and
libraries that they should write them in Lisp instead of C ...)

Reference-counted ObjC memory management is still pretty far from what
a Lisp GC offers, but can be more tractable than #_malloc/#_free.  A
reference-counted object can't be freed if it's asserted that there
are references to it and will be freed if it's asserted that there are
no references to it.  Those assertions are made via calls to #/retain
and #/release, and that's not nearly as precise as a GC proving
whether or not there are references to an object.  Those assertions
are a step or two up from #_malloc/#_free if certain conventions are
followed, e.g., when a view is installed in a window it's "referenced
once more than it had been" and when the view's removed from the
window's view hierarchy it's referenced one less time than it had
been, and that means that (for instance) the act of removing the
view from the window might cause the view to be deallocated but the
code that manages deinstallation just has to assert that there's one
less reference (and not have to decide whether or not it's safe to call
#_free.)  That's still very much manual memory management, but it can be
less error-prone than other forms of manual memory management.

It's tempting to think of ObjC objects as being close to first-class 
lisp objects.  There are other ways in which this isn't true, but the
fact that an ObjC object's lifetime isn't affected by whether or not
there are references to it (in the GC sense) pretty much destroys the
illusion of ... first-classness.  I'd certainly prefer that things
were were a bit lispier in that regard; making them that way likely
does require some level of integration between the ObjC GC and the lisp
GC, and I honestly don't know to what degree that's possible and don't
have any real sense of how long it would take to achieve that integration if
so.  (Among other things, Apple's GC seems to be written in C++ and even
trying to read C++ code gives me a bad headache ...)

There are real memory-management issues in things as they stand, but I think
that it's fair to say that at least some of those issues are less significant
in practice than they are in theory: when writing an event handler for some
class of event and some class of window it's generally not necessary or useful
to worry about whether or not "self" refers to a valid window; if you try to
maintain *my-list-of-windows*, there's a risk that that list's contents could
not only get out-of-synch but could contain "deallocated windows" and that's
generally unpleasant.

Apple offers a tool (called "leaks", of all things) which tries to identify
blocks of #_malloc'ed memory (including ObjC instances) that aren't externally
referenced.  It finds a few dozen such leaks when run against the CCL IDE;
about half of these are malloc'ed class names that may not need to be malloc'ed.
If the number and size of these leaked blocks increases over time, then that'd
be a concern; if it doesn't, then it's a very minor concern.

I'm a little more concerned about the possibilities that the IDE code retains
some things too long (possibly because we're not consistent in adhering to ObjC
object ownership conventions) and that we're referring to ObjC objects as if
they're first-class objects despite the fact that they're not.  The first of
these things is something that the bridge could and should help with more than
it does; avoiding the second class of problem involves recognizing that things
aren't quite as lispy as they seem and shouldn't be treated as if they are.

>        2. The same hold true for
>           ?  (make-instance 'ns:ns-object)
>           This will leak
>        3. The same holds true for:
>           ?  (defclass test (ns:ns-object)
>               ()
>               (:metaclass ns:+ns-object))
>           ? (make-instance 'test)
>           This will leak
>         4. The following, with *v* an Objective C class instance
>            e.g. ns-text-field is a bad idea:
>           ?  (defclass test (ns:ns-object)
>                ((text :accessor text :initform "Hallo"))
>                 (:metaclass ns:+ns-object))
>           ?  (objc:defmethod (#/controlTextDidChange: :void)
>                ((self test) (notification :id))
>                 ; do something with (text self)
>                 )
>           ?  (#/setDelegate: *v* (make-instance 'test))
>           Because the instance of 'test will be garbage collected
>           without the knowledge of objectice C runtime.  The AppKit
>           framework will happily call methods on the delegate
>           which in the lisp world does not exist anymore.
>           (of course this also leaks according to point 2.)
> Kind regards,
> Wim Oudshoorn.
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel

More information about the Openmcl-devel mailing list