Freeing Dynamic Structure does not release memory
Environment: uname -a: 2.6.38 #18 Thu Apr 28 12:38:48 CEST 2011 armv5tejl GNU/Linux
GCC:
gcc -v
Using built-in specs.
Target: arm-linux-gnueabi
Configured with: ../src/configure -v --with-pkgversion='Debian 4.4.5-8' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.4 --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --disable-sjlj-exceptions --enable-checking=release --build=arm-linux-gnueabi --host=arm-linux-gnueabi --target=arm-linux-gnueabi
Thread model: posix
gcc version 4.4.5 (Debian 4.4.5-8)
In the code below, I seem to be doing everything correctly, yet memory that's allocated is apparently not being freed. My structure method is not a linked list.
I say "apparently", understanding that the garbage collector will take action any time in pleases. Still, as I see RSS rise incrementally when my test code runs, and as it is allocating the same amount of space in each test, I should think the allocation would be re-used.
My code implements a simple dynamic structure:
struct _aemErrors
{
int stored; // true = stored to sql
int mailed; // true = mailed to alert
int nType; // LOG_DEBUG LOG_INFO...
int time; // error triggered timestamp
int line; // line in file that triggered
char *cFunction; // function that triggered
char *cDesc; // description of problem
};
struct _aemErrors **aemErrors;
int aemErrorsCount;
The structure has two char pointers which, when used, are created with either malloc() or strdup()
Each record of the structure is initialized with this:
int AddaemError(void)
{
++aemErrorsCount;
aemErrors = (struct _aemErrors **)realloc(aemErrors, (aemErrorsCount+1) * sizeof(struct _aemErrors *));
aemErrors[aemErrorsCount] = (struct _aemErrors *)malloc(sizeof(struct _aemErrors));
return(aemErrorsCount);
}
So:
int main(int argc,char **argv)
{
// initialize the structure
aemErrors=NULL;
aemErrorsCount=-1;
int nPtr=0;
int nLoopCount=0;
while (nLoopCount<100)
{
for (nPtr=0;nPtr<1000;nPtr++)
{
nPtr=AddaemError();
aemErrors[nPtr]->stored=false;
aemErrors[nPtr]->mailed=false;
aemErrors[nPtr]->nType=LOG_ALERT;
aemErrors[nPtr]->nTime=time(NULL);
aemErrors[nPtr]->line=0;
aemErrors[nPtr]->cFunction=strdup("ThisIsATest");
aemErrors[nPtr]->cDesc=strdup("ThisIsATest");
}
FreeaemErrors();
sleep(5);
++nLoopCount;
}
return(0);
}
As the loop spins I see RSS rise accordingly. I am using both an internal memory state tester (not shown: reads proc filesystem data) and also use an external shell process that encapsulates the runtime and gives me memory performance data each second. Note: the problem occurs without monitoring, so I know that it's not effecting the result.
Now, I want to free everything:
// cleanup the dynamic structure
int FreeaemErrors(void)
{
if (aemErrors==NULL)
return(true);
int i=0;
printf("FreeaemErrors():Count=%i\n",aemErrorsCount);
for(i = 0; i <= aemErrorsCount; i++)
{
free(aemErrors[i]->cFunction);
free(aemErrors[i]->cDesc);
free(aemErrors[i]);
aemErrors[i]->cFunction=NULL;
aemErrors[i]->cDesc=NULL;
aemErrors[i]=NULL;
}
printf("Done. Free root\n");
free(aemErrors);
aemErrors=NULL;
aemErrorsCount=-1;
printf("Returning\n");
return(true);
}
So I issue FreeaemErrors(); and then wait a few seconds, watching memory. It does not decrease.
And the next time I run the populate loop, adding another 1000 records to the supposedly clean structure, RSS rises again.
I'm somewhat flummoxed at the moment.
Ideas, anyone?
Thanks for your inputs here.
After quite a lot of testing with various structure sizes, 开发者_StackOverflow社区I found that once we reach a certain size, and then free everything, the RSS settles back to nearly where I expected it to be. But not quite.
I learned a bit more about memory fragmentation and how a program can bloat when, as in my example, realloc() occurs because contiguous memory for the new allocation is unavailable.
Responding to this problem involved (a) initializing my structure size to hold a block of n elements (n being 40 to 100, depending on the needs); (b) when the structure needs to grow (we ran out of the initial allocation) I make a duplicate of the original structure, free the original completely, then allocate a new one of size n + increment_size, then initialize the new one from the old, then free the old. In a case where a new record request is within the allocation count, all my function does is return the current allocation number.
The new scheme does not use realloc() at all. Given the problem, I consider this a plus.
Complicated, and perhaps it will be slow in some cases, but at least memory seems to be in check.
Freeing memory with free
or realloc
doesn't mean at all that it is returned to the system. The heap machinery will just keep these pages somewhere to have them available for the next allocation. If you really want to know if you free all your memory correctly use a tool like valgrind. This will tell you "postmortem" if your process freed all the memory it allocated, or if not it will point you to the locations in your code that allocate without freeing afterwards.
精彩评论