Channels ▼
RSS

C/C++

Cocoa Memory Management

Source Code Accompanies This Article. Download It Now.


The Autorelease Pool

An autorelease pool is an instance of the Cocoa class NSAutoreleasePool. This pool is used to contain other Cocoa objects that were given [autorelease] messages. Once stored in the pool, these objects remain valid until the pool itself is eliminated. The autorelease pool has a number of unique characteristics. For one thing, you can only dispose of the pool by explicitly sending it a [release] message. Any attempts to send either a [retain] or an [autorelease] message to the pool generates an exception.

Another aspect of the pool is that you can only add objects to the pool. You cannot take objects out of the pool. Also, you cannot send another [release] or [autorelease] message to an object that is already in the pool. If you do so, the pool generates an EXC_BAD_ACCESS error. The NSAutoreleasePool class is opaque; its internal structure is a black box. Short of using a low-level debugger, you have no way of telling how each autoreleased object is stored in the pool. Because of this opaqueness, you should only assume that all autoreleased objects would eventually be discarded. Never make any assumptions on how or in what order the disposal would occur.

When to Create Your Own Autorelease Pool

Cocoa applications that are based upon the Application Kit framework always have one autorelease pool available. This pool is created at the beginning of the event loop and is periodically eliminated at the end (see Figure 1).

Figure 1: The default autorelease pool.


In most situations, the default autorelease pool is sufficient. However, you can instantiate additional pools when necessary. Each instantiated pool is then arranged as a LIFO stack with the latest pool at the top and the default pool at the bottom (Figure 2). Any Cocoa object that received an [autorelease] message would be added to the topmost pool.

Figure 2: The autorelease pool stack.


So when should you instantiate additional autorelease pools? Here are a number of design scenarios where instantiating additional pools would be useful:

  • Building a Cocoa application that does not use the Application Kit framework; for example, a command-line utility that makes use of the Foundation Kit framework.
  • A detached thread based on either NSThread or pthread that makes use of Cocoa objects.
  • An application or thread that generates and disposes of a large number of autorelease objects.

In short, you need to instantiate at least one autorelease pool if you plan to have Cocoa objects without the benefit of the Application Kit framework.

Instantiating Autorelease Pools

Listing Ten shows how to instantiate and dispose of your autorelease pool. Apple recommends that the pool needs to be discarded in the same context in which it was instantiated. For example, if the pool is instantiated at the start of a loop, it needs to be eliminated at the end of that same loop. Also, if the pool is instantiated at the beginning of a method, it needs to be eliminated at the end of the method. Failing to dispose of the pool correctly prevents its cache of autorelease objects from being eliminated as well. This results in a potentially large memory leak.

Listing Ten

NSAutoreleasePool       *aPool;
// instantiate the pool
aPool = [[NSAutoreleasePool alloc] init];

/* Create and autorelease a lot of objects here */
// dispose the pool
[aPool release];

You also have to make sure that you dispose of the autorelease pools in the same order in which they were instantiated. For example, if you have six autorelease pools instantiated, sending a [release] message to pool 6 results in only that pool being discarded (see Figure 3).

Figure 3: Disposing autorelease pool 6.


However, if you send a [release] message to pool 4 (Figure 4), pools 5 to 6 will also be discarded. If those pools still have objects with pending retains, those objects will not get discarded.

Figure 4: Disposing autorelease pool 4.

Memory Zones

A memory zone is a region of page-aligned memory used by a Cocoa application to store its Cocoa objects. Unlike the traditional application space, a memory zone is allowed to grow whenever necessary. Mac OS X creates a default memory zone for each Cocoa application. Cocoa objects that were instantiated using the [new], [alloc], or [copy] messages are allocated memory from the default zone. Although you do have the option of creating additional zones, the default zone is fast and efficient enough for most purposes.

There are scenarios where creating additional memory zones may be desirable. For instance, if a Cocoa object instantiates and destroys a lot of objects within its scope, the default zone could get severely fragmented (Figure 5); this results in a performance hit. Using independent memory zones for storing/disposing large numbers of objects prevents the default zone from being severely fragmented.

Figure 5: Fragmenting the default memory zone.


Another scenario occurs when the default memory zone has grown to at least two pages long. If two objects (say, obj3 and obj5) were instantiated, these objects may be located relatively far apart from each other in the default zone (Figure 6). This becomes an issue if a page fault occurs and page2 is written to the disk.

Figure 6: When the default zone grows too large.


When obj3 tries to access obj5, a page fault has to occur, causing page2 (containing obj5) to be read from the disk. If this page fault frequently occurs, it translates into a noticeable performance hit. A worse-case scenario would be where reading page2 from the disk generates another page fault that may cause page1 to be written to the disk. Creating a separate memory zone to store obj3 and obj5 keeps both objects within the same page. If a page fault occurs, the zone containing both objects is written to the disk. Also, when obj3 is accessed, the resulting page fault causes the same zone (again, containing both objects) to be read from the disk.

One disadvantage of creating additional zones is that it increases the memory footprint of the application. You are also responsible for keeping track of any objects that were allocated to the zone, and making sure that the zone is empty before you dispose of it. Due to these disadvantages, creating additional zones should be considered only for optimization purposes.

To create your own memory zone, use the NSCreateZone function:

NSZone *theZone = 
          NSCreateZone(8192, 1024, TRUE);

This function takes three parameters: the initial size of the zone, the amount that the zone grows or shrinks, and whether the zone should reclaim memory. If it was able to create a new zone, it returns an opaque data structure called an NSZone; otherwise, it returns a NIL.

When you instantiate a new Cocoa object, use the [allocWithZone] message for the object to allocate its memory from my memory zone (Listing Eleven). Passing a NIL to the same message would cause the object to use the default zone as its allocation source. To create copies of an object, use the [copyWithZone] or [mutableCopyWithZone] whenever appropriate.

Listing Eleven

NSZone            *theZone = NSCreateZone(8192, 1024, FALSE);
NSString          *theStr, *theCopy;
NSMutableString   *theMutableCopy;

// allocating an NSString from the custom zone
theStr = [NSString allocWithZone:theZone];
[theStr init];

// copying NSString using the custom zone
theCopy = [theStr copyWithZone:theZone];

// copying a mutable NSString using the custom zone
theMutableCopy = [theStr mutableCopyWithZone:theZone];

You can also use the NSRecycleZone function (Listing Twelve) to free up any unused memory contained by the zone. The function also preserves any objects stored in the zone that still have valid references.

Listing Twelve

NSZone            *theZone = NSCreateZone(8192, 1024, FALSE);
 ... 
// allocate and dispose Cocoa objects
 ... 
// free all memory used by the zone
NSRecycleZone(theZone);
 ... 
// destroy the zone completely
malloc_destroy_zone(theZone);

Finally, if you want to completely dispose of the memory zone, use the system function call malloc_destroy_zone(). This function frees up all memory contained by the zone and disposes of the zone as well. It does this regardless of whether the zone still has objects with valid references. It is your responsibility to ensure that you have discarded any objects you have stored in that zone before invoking malloc_destroy_zone(). Never use malloc_destroy_zone() to dispose of the default memory zone.


Jose is a engineering consultant in Vancouver, BC. He can be contacted at anarakis@kagi.com.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video