Just for the curious me: What was the design decision behind the GC solution? Was refcounting that bad? Refcounting gives a more global speed hit indeed, but it's more deterministic and you wont run into (probably) long halts during GC. (Java programs often suffer from this, and it results in bad latency).
I'll answer this one, since I'm the one responsible for it.
Refcounting has three big issues:
1) It's expensive 2) It's error-prone 3) It misses circular garbage
#2 was the big one, so I suppose it's in the list wrong, but I'm feeling too lazy to edit. If you watch the changelogs for the P languages (perl, python, and ruby) you'll find that for almost every release there's a refcount bug patched, sometimes more than one. Throw in the problems that extensions have and you'll find a *lot* of refcount problems.
The expense is non-trivial as well. Yeah, it's all little tiny bits of time, but that adds up. It's all overhead, and useless overhead for the most part.
The circular garbage thing's also an issue. Yes, there are interesting hacks around it (python has one -- clever, but definitely a hack) that essentially involves writing a separate mark&sweep garbage collector.
The thing is, there aren't any languages we care about that require true instant destruction -- the ones that care (or, rather, perl. Python and Ruby don't care) only guarantee block boundary collection, and in most cases (heck, in a near-overwhelming number of cases) timely destruction is utterly irrelevant to the code being run. Yeah, you *want* timely destruction, but you neither need nor notice it in most program runs, since there's nothing that will notice.
Having been too deep into the guts of perl, and having written more extensions in C than I care to admit to, I wanted refcounting *dead*. It's just spread across far too much code, tracking down errors is a massive pain, and, well, yech. Yes, non-refcounting GC systems are a bit more complex, but the complexity is well-contained and manageable. (I wrote the first cut of the GC system in an afternoon sitting in my local public library) There's also the added bonus that you can swap in all sorts of different GC schemes without disturbing 99% of the code base.
There are a couple of features built into the codebase to reduce GC pauses. (Though none of these are, strictly speaking, required by the semantics parrot expresses)
Firstly, we don't allocate base structures (PMC and buffer structs) from random memory -- they all come out of arenas, which leaves us with a small and relatively restricted set of places to scan for objects. There are a number of operations that the GC system needs to do full sweeps for, and keeping things tight together makes that a lot easier.
We also have a split GC system (or, at least we do now -- it might not stay). Looking for dead objects is a separate operation from looking for dead memory. This is generally a win for us, since *generally* we're chewing through the memory hanging off of objects much faster than we are the objects themselves. This isn't characteristic of all languages, certainly, but for perl and it's cohorts it is.
The PMCs themselves are designed to reduce the number of objects that have to actually exist (though cutting out the keyed version of all the binary ops reduced the effectiveness of it a bit) since your aggregates don't have to actually instantiate the things inside them if they don't want to. A 10 million element integer array can be represented with exactly one PMC.
Finally we're really, *really* picky about where pointers to objects can be put, and I think this is the place where we get a win over other GC schemes, and one of the spots where Java traditionally falls down. If pointers to objects can be anywhere, it complicates the GC system a lot -- you have to assume that any random blob of memory *may* hold pointers to objects. It probably doesn't, but... how can the GC know? Generally it can't, since it just doesn't have sufficient knowledge of the contents, so it has to be careful, and careful costs. For us, the only spot where we have to be conservative is with the stack, the rest is all exact for GC purposes, which is nice, and there are a few common special cases (like the 'this is an array of PMC pointers' one)
All this does make it more expensive, on a per-incident basis, to guarantee timely destruction. Scope exit, if there are extant objects that need timely destruction, has a lot more one-shot overhead than a refcounting scheme does because you do need a full sweep but... we make it so that full sweeps are relatively cheap (with the known pointer location stuff) and have to go through a relatively small number of objects (since most of your objects will end up in aggregates, and we're trying to make it so that aggregates don't have to actually contain objects too often).
For regular GC sweeps we can cut down on the pauses with generational collectors and other such interesting things, which can be plugged in mostly seamlessly. Right now we're usually in stop-the-world mode but heck we compile parrot with *no* C compiler optimizations too, given that things are all in development as it is.
Also, with all this stuff, people are going to find timely destruction is less useful than they might want, what with threads and continuations, which'll screw *everything* up, as they are wont to do. I know I've been making heavy use of continuations myself, and this is for a compiler for a language that doesn't even have subroutines in it. Continuations screw everything up, which is always fun, for unusual values of 'fun'.
I really need to go profile perl 5 some time to get some real stats, but I think it's likely that most programs (well, most programs I run at least) have less than 0.1% of the variables with destructors, and maybe one or two variables *total* that have a need for timely destruction. (And most of the time they get cleaned up by global destruction, which makes 'em not actually timely cleaned up)
You said, that most languages will have refcount semantics, just sounds funny for me to implement a GC then.
Actually most languages won't have refcount semantics. Perl 5's the only one that really guarantees that sort of thing now, though I think it's in there for perl 6. I doubt the python, ruby, Lisp, or Tcl compilers will emit the cleanup-at-block-boundary sweep code.
--
Dan
--------------------------------------it's like this------------------- Dan Sugalski even samurai [EMAIL PROTECTED] have teddy bears and even teddy bears get drunk