Avoiding Garbage Collector: Delphi and Apple side by side
By A.Bouchez on 2011, Thursday December 8, 10:09 - Pascal Programing - Permalink
Among all trolling subject in forums, you'll find out the great Garbage Collection theme.
Fashion languages rely on it. At the core of the .Net and Java framework, and all scripting languages (like JavaScript, Perl, Python or Ruby), you'll find a Garbage Collector. New developers, just released from schools, do learn about handling memory only in theory, and just can't understand how is memory allocated - we all have seen such rookies involved in Delphi code maintenance, leaking memory as much as they type. In fact, most of them did not understood how a computer works. I warned you this will be a trolling subject.
And, in Delphi, there is no such collector. We handle memory in several ways:
- Creating static variables - e.g. on the stack, inside a
classor globally; - Creating objects with
classinstances allocated on heap - in at least three ways: with atry..finally Freeblock, with aTComponentownership model in the VCL, or by using aninterface(which creates an hiddentry..finally Freeblock); - Creating reference-counted variables, i.e.
string,array of,interfaceorvariantkind of variables.
It is a bit complex, but it is also deadly powerful. You have several memory allocation models at hand, which can be very handy if you want to tune your performance and let program scale. Just like manual recycling at home will save the planet. Some programmers will tell you that it's a waste of cell brain, typing and time. Linux kernel gurus would not say so, I'm afraid.
Then came the big Apple company, which presented its new ARC model (introduced in Mac OS X 10.7 Lion) as a huge benefit for Objective-C in comparison with the Garbage Collection model. And let's face it: this ARC just sounds like the Delphi memory model.
What is this ARC model?
No, ARC is not only an old packer format, nor the company responsible of solid waste removal and recycling services for Chicago and suburbs - this acronym stands for Automatic Reference Counting.
Formerly, class instances used to be manually handled in Objective
C code. You had to call explicitly the Release method
to decrement its reference count. When an object's reference count is
zero, it is deallocated.
This sounds just like our well known reference-counting mechanism included
in Delphi, for string, array of,
interface or variant. What is new with ARC is that,
as with these Delphi types, the compiler will generate the
release code. And all those kind of variable instanced are filled with
zero (this is not the same as the famous Zeroing
Weak pointers feature) - which is implemented by
_InitializeRecord() and _FinalizeRecord() in
low-level System.pas unit. Of course, the ARC implementation is
certainly more sophisticated than the basic implementation in the
Delphi compiler: it is told (at least from the marketing paper point
of view) to use some deep knowledge of the software architecture to provide an
accurate access to all instances. Whereas the Delphi compiler just relies on a
out-of-scope pattern.
There is a very interesting analysis of the ARC design available on Ars Technica, which is worth reading. Especially on modest hardware (I do not know if it is honest when this concept is applied to modern smartphones, which are much more powerful than the PCs on which the first Delphi version was running on), Apple's experts say it does make a difference, in term of memory use and program latency - the UI won't glitch during background garbage recycling.
So what about the issues?
This just sounds too much perfect. And there is no perfection on earth, nor in computing.
One common problem with reference counting is the potential circular reference issue.
This occurs when one object has a strong pointer to another, but the target object has a strong pointer back to the original. Even when all other references to these objects are removed, they still will hold on to one another and will not be released. This can also happen indirectly, by a chain of objects that might have the last one in the chain referring back to an earlier object.
This is where the ARC's Zeroing Weak pointers comes to mind. When
you read the official reference
material at llvm.org, there are also some other potential issues, but the
__weak ownership qualifier is the main point of this document.
The implementation pattern is a bit more complex that the one used for
Delphi's interface (taken from Ars
Technica):
The "zeroing" part means that weak references will be set to nil when the object they reference is deallocated. (Under ARC, all object pointers are initially set to zero.) Under normal circumstances, an object shouldn't be deallocated if there are still outstanding references to it. But since weak references don't contribute to an object's reference count, an object can be deallocated when there are outstanding weak references to it. When this happens, the automatic zeroing of the outstanding weak references prevents them from becoming dangling pointers. (In Objective-C, sending a message to nil is a no-op.)
As far as I understood the problem, simple types like our reference-counted
Delphi variables solve the circular reference issue by providing a
copy-on-write behavior. But the interface kind of variable, in its
current state, may suffer from this issue.
Since I want to use interface everywhere in our next version of mORMot (to
implement a true Domain-Driven architecture
- including SOLID
principles), some strict rules may be defined in its usage, not to suffer from
similar issues. To my understanding, as soon as you use interfaces in the
internal scope of methods (i.e. stack-allocated), you may never suffer for
circular reference.
Additional investigation is certainly needed.
Your feedback is warmly welcome on our forum!