Thread overview
Garbage collection vs. reference counts
Oct 03, 2001
John Nagle
Oct 05, 2001
Walter
Oct 07, 2001
John Nagle
Oct 05, 2001
Tobias Weingartner
October 03, 2001
   I've proposed a way to tighten up C++ using reference-counted
allocation plus compiler support for temporary, immutable raw
pointers.  See

    http://www.animats.com/papers/languages/index.html

This turns out to be a tough retrofit to C++, but might be worth looking at for D.

   Garbage collected environments introduce finalizers, which
have terrible semantics.  They're ugly enough in Java, but
take a look at "Microsoft Managed Objects for C++", which
shows where the combination of raw pointers, garbage collection,
and finalizers leads.  You can "return an object to life" from
the finalizer, leading to some strange semantics.

   Incidentally, conservative garbage collection runs into problems
as the address space fills up.  With 32-bit pointers, you have a
4GB address space.  You're likely today to have a significant
fraction of that space with real memory behind it.  So the odds
of a random bit pattern being construed as a pointer become
quite high.  And the transition to 64 bits on the desktop is
still a ways off.

					John Nagle
					Animats
October 05, 2001
John Nagle wrote in message <3BBB4600.C382802@animats.com>...
>   I've proposed a way to tighten up C++ using reference-counted
>allocation plus compiler support for temporary, immutable raw pointers.  See
>
>    http://www.animats.com/papers/languages/index.html
>
>This turns out to be a tough retrofit to C++, but might be worth looking at for D.

Thanks. I did something similar in C years ago with the __handle pointers.


>   Garbage collected environments introduce finalizers, which
>have terrible semantics.  They're ugly enough in Java, but
>take a look at "Microsoft Managed Objects for C++", which
>shows where the combination of raw pointers, garbage collection,
>and finalizers leads.  You can "return an object to life" from
>the finalizer, leading to some strange semantics.


I never liked the resurrection possibility, either.


>   Incidentally, conservative garbage collection runs into problems
>as the address space fills up.  With 32-bit pointers, you have a 4GB address space.  You're likely today to have a significant fraction of that space with real memory behind it.  So the odds of a random bit pattern being construed as a pointer become quite high.  And the transition to 64 bits on the desktop is still a ways off.


Reference counting has its own problems, like inability to deal with cycles.


October 05, 2001
In article <3BBB4600.C382802@animats.com>, John Nagle wrote:
> 
>    Incidentally, conservative garbage collection runs into problems
> as the address space fills up.  With 32-bit pointers, you have a
> 4GB address space.  You're likely today to have a significant
> fraction of that space with real memory behind it.  So the odds
> of a random bit pattern being construed as a pointer become
> quite high.  And the transition to 64 bits on the desktop is
> still a ways off.

There are ways around this.  Read some of the more
advanced garbage collection papers out there.

--Toby.
October 07, 2001
Walter wrote:
> 
> John Nagle wrote in message <3BBB4600.C382802@animats.com>...
> >   I've proposed a way to tighten up C++ using reference-counted
> >allocation plus compiler support for temporary, immutable raw pointers.  See
> >
> >    http://www.animats.com/papers/languages/index.html
> >
> >This turns out to be a tough retrofit to C++, but might be worth looking at for D.
> 
> Thanks. I did something similar in C years ago with the __handle pointers.

   It's easy to do.  There have been many (too many) smart
pointer libraries for C++ The problem is doing it safely and
efficiently.  That's what I've been trying to address.

   The key idea is introducing local references which can't
outlive the object they reference.  Once such a local reference
has been created, its use requires no further reference
counting.  That's what the "auto pointer" scheme I proposed
is all about.

   From a compiler perspective, it's also worth considering the
automatic introduction of such references, as a way of hoisting
reference counting out of loops.

> I never liked the resurrection possibility, either.

   Other troubles with finalizers involve locking (finalizers
introduce concurrency, since they can in be called from another
thread) and exceptions (who catches an exception from a finalizer?).
But the big problem is that finalizers are called late, from GC,
and so can't be used for holding open descriptors, locks, and
other non-memory assets which must be released when unneeded.

   Having both finalizers and destructors in the same language is
even worse.  If you have finalizers only, you need some way to
have actions take place at scope exit, like Common LISP's
(WITH_OPEN_FILE file block) construct.

> Reference counting has its own problems, like inability to deal with cycles.

   Cycles are annoying, but at least have comprehensible semantics.
Weak pointers (as used in Perl, not as used in Java) help deal
with cycles.  True cycles of peers are rare; the most common case
is a back pointer to a superior object.  Such back pointers should
be weak pointers.  That deals with the most common case effectively.

   When you truly have interlinked peers, the proper approach is
to have them all owned by a collection in some superior object,
and then have all the inter-object links be weak.

   Weak and strong pointer semantics are worth thinking about.
First, you can't dereference a weak pointer; you have to take
a strong pointer from it first.  This may be done through an
implicit conversion, but it has to be done, so that the target
object can't go away while referenced.  Attempts to dereference
invalid weak pointers must throw.

   If you do it that way, destructor sequencing is automatically
correct.  If A has a strong pointer to B, and B has a weak
pointer to A, then removing the last strong reference to A
causes A's destructor to be called.  A's destructor can make
calls to B's methods, but B's weak pointer back to A is invalid
(because A's strong reference count is 0) so B can't call A's
methods while A's destructor is running.  So you can't get
accidental re-entry into an object during destruction.

   So reference counted weak and strong pointers can be
efficient, safe, and have clean semantics.

					John Nagle
					Animats