gnustep-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Memory leaks galore!


From: Fred Kiefer
Subject: Re: Memory leaks galore!
Date: Wed, 20 Feb 2013 23:15:56 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130105 Thunderbird/17.0.2

Great to see that somebody else uses valgrind to dig through GNUstep memory management. I do so regularly, but it always helps if more do it.

Valgrind is a very powerful tool and you will need to learn how to use it properly. As Ivan already pointed out, most of the loss records you got are completely harmless. It is completely normal for a library like GNUstep to set aside some memory at startup. Almost all libraries do so. Some add a special hook to clean up that space on shot down, when a memory debugger gets used. Up to now we didn't find this necessary in GNUstep, but if you want to provide a patch for that, it will be welcome. Valgrind knows about that situation and has a special solution for memory being leak by libraries. It is called suppression files and allows you to suppress these warnings. Nobody has written a suppression file for GNUstep base up to now, but again you are welcome to do so.
One simple way to do so is to use your example program, see:

http://valgrind.org/docs/manual/manual-core.html#manual-core.suppress

or

http://wiki.wxwidgets.org/Valgrind_Suppression_File_Howto

In your case, just one entry for GSPrivateBuildStrings should do.

Hope this helps,
Fred

On 20.02.2013 20:50, Jamie Ramone wrote:
On Wed, Feb 20, 2013 at 3:50 PM, Ivan Vučica <address@hidden> wrote:

On 20. 2. 2013., at 19:00, Jamie Ramone <address@hidden> wrote:

On Wed, Feb 20, 2013 at 2:20 PM, Ivan Vučica <address@hidden> wrote:

Most of the losses seem to be in GSPrivateBuildStrings().
   http://svn.gna.org/svn/gnustep/libs/base/trunk/Source/externs.m
which says it's called from +[NSObject initialize] solely to optimize
existing global constant strings.

So that's alright; you don't want these released until the end of the
program, and perhaps not even then. In a discussion from, I think, a year
ago, David advocated avoiding deinitalization and releasing of as much
stuff at the end of the program as realistically possible. Imagine
allocating a gigabyte of small objects. Instead of deallocing them, you
simply leave them be. They'll get deallocated at the end of the program,
and you'll avoid potentially swapping a gigabyte of virtual memory from
disk into RAM. So I guess that not bothering to use something like C++
destructors or something with atexit() to hack deallocing at the end of the
program is the result of this (good and smart) opinion.


I'm not sure I follow the deallocation-swap argument. Why would a block of
memory that was swapped out be swapped back in when deallocating it? My
understanding is that free () simply changes some values in the c runtime
to mark that block of memory as available.


That is my understanding of free() too. The problem is not with freeing
one massive 1gb block. But imagine freeing 1gb of SMALL objects. If
-dealloc is more complex and references ivars, you need to swap them in,
free subobjects, stuff like that.


Oh, OK, I see what you mean now. Yes, that case is an obvious swap-monsoon.
However, the proper way to deal with that problem is not causing it in the
first place. Plus, main memory is progressively abundant. It gets harder
and harder to use up all the memory so as to cause even a single swap, at
least by the data structures themselves. I don't even have a swap
partition, what with having 8 GB and all. I don't know, it seems like a not
very common case.

You will have to look up the old thread, or wait for someone with more
understanding to chime in.

But be that as it may, the main problem I have with this kind of lazy
deallocation is that it makes my own memory leaks much harder to find as
they get camouflaged by these. Can this behavior be turned off in favor of
a traditional bracketed approach?


If my understanding is correct, the code isn't even there. You could write
something using a C++ destructor trick using a global variable that will
get destructed at exit no matter what (well, unless you have a very nasty
crash). But you'd have to write code to perform these free()s.

There is probably some way of telling Valgrind to ignore certain leaks,
right?


I'll check the documentation but I don't think that's possible. Valgrind
checks for alloc/free balance. While you can change the level of verbosity,
I don't think it's possible to get it to filter (what it sees as) leaks.
But like I said, I have to check the documentation just to be sure.



The only case where you want to dealloc stuff is where you do
deinitialization of network connection and similar things in
-(void)dealloc, and yesterday I ran into Apple recommendation that in clean
code you don't really want to do that in -(void)dealloc. Or should avoid it
as much as possible. I'm cargo-culting here; it sounds reasonable, but I
don't really have anything more than that.


I know, that's why I use release ;-)


You override -release to release resources at object's end-of-life? :-S


No, I release an object once I'm done using it making use of the reference
count mechanism. I'm surprised you never heard of it, it's been around for
decades :P But the reference count mechanism  guarantees that -dealloc will
eventually be called, at least in theory, where we don't have this
leave-it-to-the-os hack. Which is why I have implementations of -dealloc in
my objects.

Heck, even Apple tried to push "immediate termination" in 10.7 -- probably
precisely to cut down on shutdown times! Killing an app is faster than
gracefully shutting it down.


That's fine for some desktop environments. However, I really, really,
REALLY don't care about it not shitting down instantaneously. For my
purposes, a graceful shutdown is preferred.


I like graceful shutdown too, but am also pointing out why (some of) the
leaks are not troublesome. :-)

I'm afraid I cannot help more than this. Who knows how much Valgrind would
help with Cocoa?


Yes, I see. Thanks for all the help.

Oh, BTW, about that macro, do you mean redefine it in my code or recompile
GNUstep-base?

  More leaks seem to come from Unicode.m. Again, creation of a large static
table seems to be the cause, and is not cause for alarm.


Could more leaks be static constant NSStrings?

In any case, if possible, try configuring Valgrind to not treat these
strings as leaks. Or try replacing GS_REPLACE_CONSTANT_STRING's #define
with an empty define; it will cut down at least on these erroneous leak
reports.


OK, I'll try that. Thanks.

////////
However, I really can't see any LARGE block being allocated! Note that
out of those ~400kb of still alocated memory, 390kb are still reachable,
and did not get reported by Valgrind in the log you printed out. :-)

And perhaps they're not leaks at all, but are meant to be deallocated by
the death of the process; if you can't reliably notice increments in memory
usage over time, I'd say don't worry.

On 20. 2. 2013., at 17:58, Jamie Ramone <address@hidden> wrote:

Hi everyone. I'v been having a bit of a hard time with a program I'm
writing. I saw lots of memory leaks with valgrind so I reviewed everything,
fixed a few obvious mistakes and managed to reduce it to 1.2 MB of memory
lost. But after that I was stumped. I looked trough the code, Apple's and
GNUstep's (meager) documentation and nothing. I finally decided to run a
little test. I commented out the main function and replaced it with this:

int main ( int args, char ** arg )
{
       int errorCode = -1;
       NSAutoreleasePool * pool = nil;

       pool = [NSAutoreleasePool new];

       if ( pool != nil ) {
               errorCode = 0;
       }

       [pool release];

       return errorCode;
}

I ran it through valgrind and got this:




reply via email to

[Prev in Thread] Current Thread [Next in Thread]