views:

167

answers:

4

Hello all,

I am developing a program in Cocoa, with the hopes of using the smallest amount of memory possible. To check this, I have been using Activity Monitor. My program has a fairly large NSMutableArray containing instances of NSString, and when I release the array, I do not get all of my memory back. The following example demonstrates my problem.

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:10];

    NSLog(@"The array has no elements!"); // Activity monitor says 1.5 megs
    [NSThread sleepForTimeInterval:10];

    NSUInteger i;
    for(i = 0;i < 1000000;i++)
    {
        NSString *str = [[NSString alloc] initWithFormat:@"Number=%d, again it is: %d, one more time for added length: %d", i, i, i];
        [array addObject:str];
        [str release];
    }

    NSLog(@"We have used lots of memory!"); // Activity Monitor says 108 megs
    [NSThread sleepForTimeInterval:5];

    [array release];
    NSLog(@"We have released the array!"); // Activity Monitor says 19 megs
    [NSThread sleepForTimeInterval:5];

    [pool drain];
    NSLog(@"We have drained the pool!"); // Activity Monitor still says 19 megs
    [NSThread sleepForTimeInterval:5];

    return 0;
}

I added calls to sleepForTimeInterval so I could see and note the memory usage listed by Activity Monitor. At each pause I have listed the amount of memory I am supposedly using. I am almost sure my problem stems from a misunderstanding in the retain/release conventions. Does anyone see where I am going wrong?

+1  A: 

Dang it, I just wrote a whole answer missing your [str release] call :)

Anyway, you aren't actually using the autorelease pool, as you are manually allocing everything yourself. If you instead use [NSString stringWithFormat...] and then don't release the strings, then you will be using the autorelease pool.

Matt Greer
A: 

Memory is cleaned up between run-loops.

[NSThread sleepForTimeInterval:n] 

blocks the thread, and no run-loop processing occurs.

http://developer.apple.com/mac/library/documentation/cocoa/reference/Foundation/Classes/NSThread_Class/Reference/Reference.html#jumpTo_12

You're not specifically deallocating memory, you're simply decrementing the retain count to 0, which will allow the runtime to clean up the memory at the next iteration of the run-loop.

"Objects that have been autoreleased during that time will be destroyed at the end of the run loop cycle..."

http://parmanoir.com/When_does_autorelease_release_%3F

Dave Martorana
There is no run loop. It's just a main() function and he's managing his autorelease pools manually.
Darren
+3  A: 

Your code is correct.

I suggest using Instruments to inspect your object allocations. You should see that everything has been deallocated when you release your array.

The numbers you're seeing in Activity Monitor may be the result of some NSString memory caching performed by the Foundation, or it may simply be a function of the OS memory manager is handling your application.

Darren
The caching makes sense. Is there a way to disable it, maybe with optimization settings? Would a custom subclass potentially fix that?
Christopher O'Neill
After looking into it a bit more, it turns out it is in fact Activity Monitor. I rewrote the same code using char** and malloc, and the same issue was present. Activity Monitor just does not provide an accurate measurement of used memory.
Christopher O'Neill
Activity Monitor provides an accurate measure of memory use by the application, but not a measure of allocations. For that, use Object Alloc. The conjecture regarding memory caching is entirely wrong; there is no such string cache (but it was a good guess).
bbum
A: 

And how to deal with this memory situation?

zhyk