About

This is the M Cubed Software weblog. To find out more about us head to our about page.


Search


Feed

 Subscribe (Atom)


Archives

Lazing by the NSAutoreleasePool

Posted on 04/04/2009 at 10:21 PM in

In a reference counted environment there are times you need to release an object after it has passed out of the reach of the method or object that created it. To solve this Cocoa has the concept of an autorelease pool. A developer can set an object to be auto released, which means it is added to one of these pools which is periodically drained.

If you are creating an AppKit application, then you have an autorelease pool set up for you so you don't need to worry about it most of the time. If you set up a Foundation tool then you'll find that the Xcode template puts code to initialise and release an autorelease pool at the beginning and end of your main function.

However, a problem arises when you're creating a lot of objects at once. The obvious solution is to initialise and release objects by hand in this case, but sometimes it isn't possible. A lot of objects returned by Cocoa methods are autoreleased (by convention any object returned by a class method (other than +new or +alloc) should be autoreleased).

I had one of these situations in Code Collector Pro. Some users were having problems when pasting in the jQuery code, so I decided to investigate. I found that CCP's memory usage was jumping up by about 800MB when performing the regexes to find what code to colour. Obviously that's bad.

ObjectAlloc Instrument showing memory going from 5.12MB to 841.07MB

The problem is, there were a huge number of autoreleased objects being created, but the autorelease pool only releases all the objects it contains at the end of each cycle of the run loop. All these objects are being created on once cycle.

The solution? Make another pool just for these objects. Autorelease pools are stored in a stack (one per thread). If you initialise a new one all objects autoreleased until you release the pool will go into it. The code that was creating objects was running in a loop so it made sense to release the objects after every iteration of the loop. So I changed my code from:

for (id item in array) {
    //Perform regexes and colour
}
to:
for (id item in array) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    //Perform regexes and colour
    [pool drain];
}

This reduced my max memory usage from the 841.07MB show above to 22.33MB. You can also see below how there are many peaks and troughs in the memory taken up by objects now. These represent each iteration of the loop, where a lot of objects are created and then released.

Good use of NSAutoreleasePools in areas where you are creating lots of objects can reduce your memory footprint a huge amount, especially in loops.

(2) Comments





Comments

One other strategy to keep in mind is to count your iterations, and drain the pool every 1,000 passes (or whatever number makes sense). If you have any really performance critical loops you can avoid the slowdown of draining the pool on every pass, but also keep a handle on memory usage.

Posted by Marc Charbonneau on 04/04/2009 at  11:31 PM

This can be especially important when you’re threading. In my app, I copy a ton of data from an arbitrary source to files on disk on a second thread to allow the user interface to remain responsive. Eventually my pool filled up so much with autoreleased NSData objects, my app was getting malloc errors when my 4Gb of memory space ran out. No amount of careful -alloc and -releasing solved it, because the framework I use autoreleases everything.

The moral of the story is - despite the fact your run loop is merrily looping along, this only counts for your main thread. You still need to use this technique on other threads no matter what your main one is doing.

Posted by Daniel Kennett on 05/04/2009 at  06:39 PM

Post a Comment

Commenting is not available in this weblog entry.

<< Back to main