Re: Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful
Re: Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful
- Subject: Re: Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful
- From: John Engelhart <email@hidden>
- Date: Thu, 7 Feb 2008 01:14:10 -0500
On Feb 6, 2008, at 8:48 PM, Chris Suter wrote:
On 07/02/2008, at 12:06 PM, John Engelhart wrote:
However, consider for a moment if it was a type attribute, and
followed type attribute rules, and how this would effect the
examples cited. Off the top of my head, I think treating it as a
type attribute would have prevent every single error I've pointed
out. Hypothetically, consider if UTF8String propagated the
__strong type, and the assignment of its pointer to the ivar 'const
char *'.
The compiler would fail to compile the code, and generate an error.
I don't think it should be a type qualifier. It would mean that you
wouldn't be able to do things like:
puts ([myString UTF8String])
without getting a compiler warning.
This is a pretty debatable point, with pros and cons on each side. My
opinion is that you should not be handing pointers which require write
barriers for proper operation of the GC system to code that is not
compiled with the proper support. One could argue that the decision
to require that "all frameworks must be GC compiled/capable" makes
this policy a requirement.
Since the GC system considers everything on the stack to be a live
pointer, this has the effect of catching 99.99% use of pointers in
this fashion. While I do not have a specific example to show here, I
think you'll agree that there are occasionally times when calling a C
library function will violate these principles. Realistically, when
you call a function, that pointer vanishes down a call stack that is
surpassingly complex, and that pointer has a tendency of visiting
places you would not think applies in a particular case. If you've
used Shark.app, I'm sure you've seen some functions which end up
creating some surprisingly deep call-stacks that in turn are calling
all sorts of seemingly unrelated functions.
I don't argue that it covers 99.99% of the cases. It's that last tiny
bit that I'm writing about. I'm sure you'll agree that tracking down
errors of this nature is, politely, frustrating.
There's an example someone once used to underscore how optimistic we
can be and spectacularly misjudge the likelihood of these rare errors
occurring. It comes from the canonical example of multi-threaded/
multi-cpu programming:
x++;
I'll skip over the specifics, but the question is asked "How likely do
you think the condition is for two threads to get into a race
condition and incorrectly update 'x'? A million to one?"
While a million to one odds seems like a lot, at two gigahertz, that's
roughly 2000 times per second. That's roughly a 500 microseconds mean
time to failure rate.
Oddly, I had to add a second NSLog() in order to get some kind of
lossage, but I think it's fair to chalk this up to the semi-random
nature of allocations.
I think that would have been because the pointer returned by
UTF8String was still on the stack or in a register.
Hard to say. I think it does illustrate another point: the seemingly
random nature in which you will get bitten by these kinds of bugs.
The above example is now perfectly legal by everyones definition of
how things were under retain/release, and I correctly clear the
pointer before it goes out of scope, and demonstrates that the GC
system can, and does, reclaim live data out from under you.
I think your example is contrived. Whilst it's legal in the retain/
release world you wouldn't ever write anything like that.
Granted, my example is contrived, no two ways about it. But is that
not the point? To create a compact example which replicates the
problem? I believe I have done all that is required: demonstrate that
it is possible. Once I've done that, the sheer volume of code under
consideration essentially guarantees that this is taking place. My
practical, hands on experience suggests this (and by this, I don't
mean this particular example per se, but the ease in which it's
possible to get some of these subtle points wrong) is happening far
more frequently then you would think.
The solution to all of this is, as has already been stated, is to
understand the contract that UTF8String promises and to make your
own arrangements if you want to hang on to the value.
You're absolutely right. But my point is that, in practice, this is
not quite as clear cut as it seems.
Allow me to back way, way, way up. For the purposes of this argument,
let us not consider all the technical points that have been discussed
so far, as it's easy to get lost arguing pedantic, nuanced details.
Let's consider the GC system from a purely pragmatic point of view.
Now, the precise specifics not withstanding, you will at some time get
some small detail wrong. You will have created a bug with regards to
some GC detail. The effect of this bug, which I think everyone will
reasonably agree on, is likely to result in the collector reclaiming
memory that you have in use when you clearly didn't want it to.
The hows and whys of your bug aren't really important, but you have
done something wrong when you shouldn't have. These things happen.
My experience with these bugs has been that they consume an
EXTRAORDINARY amount of time to track down. Because of the semi-
random nature that these bugs manifest themselves in, I have found
that it's virtually impossible to find a solid set of conditions to
tickle the condition. I have found that unit tests are worthless in
trying to track down these problems. The complex interactions
required to tickle these bugs are essentially impossible to create
with unit tests. All unit tests will pass, flawlessly, and in fact it
some times may not be possible to recreate the right conditions to
trigger the bug because essentially all your variables are sitting on
the stack in the scope of the unit test.
The pragmatic effects of using Leopards GC system has been a MASSIVE
increase in the amount of time I have spent debugging problems that
have all the symptoms of "race condition" bugs, and consequently the
huge uptick in effort required to find and eliminate these bugs.
This is my warning to you (you being all of the list, or archive
reader). Setting aside all the technical points discussed, you will
eventually create a bug that in retrospect, you clearly shouldn't
have. The nature of these bugs means that it will take tremendous
effort and time to track down and correct. My experiences with the GC
system have seen the time I spend debugging explode, and in fact
dominate the time I spend developing. What's worse is that these bugs
rarely manifest themselves during development and are nearly
impossible to catch with unit tests. This means that the reliability
of "shipped code" goes right through the floor, and replicating the
bug often requires considerable interaction with an outside party and
all the difficulties that that entails.
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden