Friday, October 16, 2009

DynamicArray causing memory leak in C++ Builder

Recently I came across a memory leak which CodeGuard reported was due to DynamicArray.set_length(...).

Well, DynamicArrays are referenced counted, exactly the same way as AnsiStrings / UnicodeStrings so really there's no way you could've missed free-ing it... ... or is there?

From experience, the first thing that came to mind was that since it's a referenced-counted container - and as with the rest, it's prone to circular-references.

Consider the following code:
#include <vcl.h>
#pragma hdrstop

#include <iostream.h>


struct
TCircularRef
{

DynamicArray<TCircularRef> refs;

~TCircularRef() { cout << "here" << endl; }
};


#pragma argsused
int main(int argc, char* argv[])
{

DynamicArray<TCircularRef> base;
base.set_length(1);

base[0].refs = base;
base.set_length(0);

return 0;
}

With the 'refs' member of DynamicArray<TCircularRef> base referring back to itself (base[0].refs = base), we ended up with a circular-ref. Yes, the cirular-ref in the above example is easy to spot and would probably never occur in the real world as well. In reality though, the circular-refs are much harder to spot and assignments change during run-time, depending on the code branch and/or timing. What's worse is that the reference could be held by an object of which you pass to another library written by someone else...

Notice that the above sample code tries to break the circular-ref such that it would get cleaned-up with the line base.set_length(0)? Alas, that was to no avail as its ref-count prior to set_length would be 2, which leaves us with a ref-count of 1 after that. Can we call set_length(0) again then? No. Why? Because 'base' no longer knows about the object it once held as the first set_length(0) would've set it to point to the new memory location allocated by set_length(0).

Is there a way out? There're a few. But none to my liking. (You could Google for 'circular-reference' if you're interested to find a solution to this but it's not the intention of this article).
As I have a garbage collector library ready to be used in any projects I create with C++ Builder, that was the solution I took. I simply replaced all DynamicArray with gc_array and allocated it with gcnew_array. With that, I no longer had to care about the hairy problem of who owns the object, who's responsible to free it and whether there's a potential circular reference problem in my code. What's more, it's thread-safe, unlike DynamicArray. But that's a topic for another day.

In short, this is my advice: Avoid DynamicArray at all costs!

2 comments:

Bijayani said...
This comment has been removed by a blog administrator.
Zach Saw said...

@Bijayani

This post has nothing to do with VC++. And C++ Builder has CodeGuard built-in.

Your comment shows you have *not* read the article and was simply trying to promote your link.

I've therefore deleted your post. I will report abuse to Google should you try that again.