If you suspect your code is not using memory as efficiently as it could, the first step in determining if there is a problem is to gather some baseline data. Monitoring your code using one of the Apple-provided performance tools can give you a picture of your code’s memory usage and may highlight potential problems or point to areas that need further examination. The following sections describe the tools most commonly used for memory analysis and when you might want to use them.
The Instruments application is always a good starting point for doing any sort of performance analysis. Instruments is an integrated, data-gathering environment that uses special modules (called instruments) to gather all sorts of information about a process. Instruments can operate on your application’s shipping binary file—you do not need to compile special modules into your application to use it. The library of the Instruments application contains four modules that are designed specifically for gathering memory-related data. These modules are as follows:
The ObjectAlloc instrument records and displays a history of all memory allocations since the launch of your application.
The Leaks instrument looks for allocated memory that is no longer referenced by your program’s code; see “Finding Leaks Using Instruments.”
The Shared Memory instrument monitors the opening and unlinking of shared memory regions.
The Memory Monitor instrument measures and records the system’s overall memory usage.
You can add any or all of these instruments to a single trace document and gather data for each of them simultaneously. Being able to gather the data all at once lets you correlate information from one instrument with the others. For example, the Leaks instrument is often combined with the ObjectAlloc instrument so that you can both track the allocations and find out which ones were leaked.
After gathering data for your application, you need to analyze it. The following sections provide tips on how to analyze data using several of the memory-related instruments. For information on how to use the Leaks instrument, see “Finding Leaks Using Instruments.”
You use the ObjectAlloc instrument to track the memory allocation activity for your application. This instrument tracks memory allocations from the time your application is launched until you stop recording data. The instrument shows you the allocations in terms of the number of objects created and the size (or type) of the objects that were created. In the icon viewing mode, the instrument displays a real-time histogram that lets you see changes and trends directly as they occur. It also retains a history of allocations and deallocations, giving you an opportunity to go back and see where those objects were allocated.
The information displayed by the ObjectAlloc instrument is recorded by an allocation statistics facility built into the Core Foundation framework. When this facility is active, every allocation and deallocation is recorded as it happens. For Objective-C objects, copy
, retain
, release
, and autorelease
messages are also recorded.
In icon mode, the ObjectAlloc instrument displays a table with a listing of all memory blocks ever allocated in the application, as shown in Figure 1. The Category column shows the type of the memory block—either an Objective-C class name or a Core Foundation object name. If ObjectAlloc cannot deduce type information for the block, it uses “GeneralBlock-” followed by the size of the block (in bytes). The Net column shows the number of blocks of each type currently present in the application’s memory heap. The Overall column shows the total number of blocks of each type that were allocated, including blocks that have since been released.
The histogram bars in the Allocations (Net/Overall) column give you a graphical representations of the column data. The dark portion of the bar indicates the Net value while the complete length of the bar indicates the overall value. If you see long bars and only a portion of the bar is dark, this indicates that you are allocating and releasing similar sized blocks frequently. Rather than releasing the blocks each time, you might investigate to see whether you can allocate the blocks once and reuse them later.
Clicking the checkboxes in the Graph column changes the information displayed in the track pane next to the ObjectAlloc instrument. By default, the track pane displays a running sum of the total number of bytes allocated by the application over time. You can change this setting to display the sum of allocations for one or more specific block types to see when those allocations occurred. Steep jumps in the slope of the graph indicate places where a lot of memory was allocated quickly.
In addition to display the total number of bytes allocated, you can change the type of information displayed in the track pane by changing the settings of the ObjectAlloc instrument. Clicking the information button on the instrument displays an inspector, which you can use to choose the display options for the track pane. Changing the Style field to Allocation Density shows you the number of allocations that occurred every millisecond. Spikes in this graph also indicate places where your code was allocating a lot of memory in a short amount of time.
The Detail pane of the ObjectAlloc instrument is where you go to find specific information about each memory allocation that was made. By default, the detail pane shows the net and overall allocations for each object type. Moving the mouse over an object type in the Category column, however, reveals an arrow button that when pressed displays details about the allocated blocks of that type. You can find out when the block was allocated along with details about the code module that allocated it.
While browsing the specific block allocations, clicking the button next to the address of a given block shows you the history of the memory allocations at that address. You can find out what type of block was allocated, when it was allocated, when it was released, who did it, and when they did it. Because allocations are tracked by address, the size and category of the block is also included. You can use this view to see all the types of data that were loaded into that particular address.
In addition to icon mode, the ObjectAlloc instrument supports an outline mode that organizes memory allocations by type and then shows the call tree that was used to make those allocations. Using this mode, you can look at how many blocks of a specific type were allocated within a given call tree of your application. In addition to viewing allocations for specific types of blocks, there is an entry for all allocations that mixes all of the memory allocations together into a single call tree, so you can see the total amount of memory allocated by your code at any given time.
You can use the outline mode to identify objects that were allocated by the same portion of code, whether directly by one of your routines or indirectly through a system routine. If you see some unexpected allocations in one of your application’s routines, you might look to see why and adjust your own memory usage accordingly. You can also use this mode to see if a particular branch of your code is allocating more memory than you expect.
For Mac OS X applications, the Shared Memory instrument tracks calls to any shm_open
and shm_unlink
functions, which are used for opening and closing regions of shared memory in the system. You can use this information to find out where your application is getting references to shared memory regions and to examine how often those calls are being made. The detail pane displays the list of each function call along with information about the calling environment at the time of the call. Specifically, the pane lists the executable that initiated the call and the function parameters. Opening the Extended Detail pane shows the stack trace associated with the call.
For Mac OS X applications, the Memory Monitor instrument displays assorted statistics about system memory usage. You can use this instrument to view the memory usage trends in your application or on the entire system. For example, you can see how much memory is currently active, inactive, wired, and free. You can also see the amount of memory that has been paged in or out. You might use this instrument in conjunction with other instruments to track the amount of memory your application uses with respect to specific operations.
The MallocDebug application is an alternative to the Instruments application that you can use to inspect your program’s memory allocations and look for leaks. MallocDebug shows currently allocated blocks of memory, organized by the call stack at the time of allocation. You can use MallocDebug to determine how much memory your application allocates, where it allocates that memory, and which functions allocated large amounts of memory. It gathers data from the Carbon Memory Manager, Core Foundation object allocations, Cocoa object allocations, and malloc
allocations.
Like Instruments, MallocDebug does not require prior instrumentation of the program—that is, you don’t need to link with special libraries or call special functions. Instead, MallocDebug launches your application using its own instrumented version of the malloc
library calls. MallocDebug does not operate on applications running on iPhone or iPod touch devices.
Note: The custom malloc library used by MallocDebug may hold on to memory blocks longer than normal for analysis purposes. As a result, you should not try to gather metrics regarding the size of your program’s memory footprint while running it under MallocDebug.
MallocDebug includes a number of features you can use to refine your memory analysis:
It provides a hex-dump view for examining raw memory.
It allows you to mark off any period of execution for analysis.
It allows you to export performance data for detailed examination or for further analysis and refinement by command-line tools. The export feature gives you the freedom to look at or summarize the data in the form most relevant to your executable.
For information on how to use MallocDebug to identify memory leaks in your program, see “Finding Leaks With MallocDebug .”
After launching MallocDebug, the main window appears (Figure 2). There are three basic sections in the MallocDebug window. Information about the launched program is at the top of the window. The center portion displays the call stack browser. The bottom portion displays the memory buffer browser.
To start a new MallocDebug session, you must select and launch the application you want to analyze by doing the following:
Enter the full path to the program in the Executable field, or click the Browse button and select the program using the file-system browser.
If you want to run the executable with command-line arguments, enter them in the Arguments field.
Click the Launch button.
MallocDebug launches the program and performs an initial query about memory usage. Further updates occur whenever you press the Update button.
The main focus of memory analysis in MallocDebug is the call stack browser (see Figure 2). This browser shows you where memory allocations occurred by gathering stack snapshots whenever one of the malloc library routines was encountered. Figure 3 shows a sample set of data for calls to the malloc
routine.
MallocDebug coalesces the call stack information it gathers into a call tree by overlapping equivalent sequences of functions. It then presents this information in the call stack browser. The call stack browser has three display modes: standard, inverted, and flat. Each display mode presents the data in a different way to help you identify trends. You can choose which mode you want from the left-most pop-up menu and toggle back and forth as needed.
Standard mode presents each call stack hierarchically from the function at the top of the stack (for instance, main
) to the function that performs the allocation: malloc
, calloc
, and so on. Each element of the browser shows the amount of memory that has been allocated in the call stack involving that method or function. Figure 4 illustrates the structure of the call stack in standard mode.
Inverted mode reverses the hierarchy of standard mode and shows the call tree from the allocation functions to the bottom of each stack. This mode is useful for highlighting the ways in which specific allocation functions are called. By seeing all the calls to malloc
or the Core Foundation allocators, you can more easily detect wasteful patterns in lower-level libraries. Use inverted mode if you’re working on a low-level framework or if you want to focus on how you’re calling malloc
in your own code. Figure 5 illustrates the structure of the call stack in inverted mode.
Flat mode shows memory usage for every method and function of an application in a single list, sorted by allocated amount. All of the instances of a function call are collapsed into one browser item corresponding to that function. A function’s memory use includes the sum of all the allocations performed in that function and all allocations performed in functions that it calls. This allows you to see the total amount of memory allocated by a specific function and every function it called, not just those at the top or bottom of the call stack.
The analysis mode pop-up menu (located to the right of the viewing-mode pop-up menu) affects the type of allocations that are displayed in the call stack browser. You have several options:
All Allocations Gives you the call trees for all currently allocated buffers in your application.
Allocations since Mark Displays functions and methods in which an allocation has occurred since launch time or the last mark. See “Taking a Snapshot of Memory Usage” for information on how to display allocations over a specific period of time.
Leaks This item displays a call tree showing leaked memory blocks in your program. For further discussion of this analysis mode, see “Finding Memory Leaks.”
Overruns/underruns Displays a call tree with a list of buffers that were written to incorrectly, caused by writing to memory before or after the buffer boundary. If the program wrote past the end of a buffer, a right arrow (>
) appears by the buffer. Similarly, if the application wrote before the start of a buffer, a left arrow (<
) appears by the buffer. For more on MallocDebug’s memory-detail features, see “Analyzing Raw Memory.”
When you launch a program with MallocDebug, the main window displays the allocation activity that occurred during launch time. When you click the Update button, MallocDebug shows memory usage up to the current point in time. If you want to display allocations from a particular point in time, you can do the following:
Press the Mark button.
Exercise a portion of your program.
Select the "Allocations from mark" item from the analysis mode pop-up list.
MallocDebug shows the buffers allocated since the mark was set. Note that MallocDebug displays only the buffers that are still currently allocated, so you will see only those buffers allocated since you clicked the Mark button that have not been freed.
When you select an allocation buffer in the call stack browser, the memory buffer list (shown in Figure 2) might show one or more lines of data. Each line in this list represents a block of memory allocated by the currently selected function or by a function eventually called by that function. Each line contains the address of the buffer, its size (in bytes), and the zone in which it was allocated. Double-clicking one of these lines opens the Memory Viewer Panel window, which you can use to inspect the contents of memory at that location.
If code attempts to write before the start or past the end of a buffer, the memory buffer list shows an appropriate indicator in the Status column. If bytes were written before the buffer, the column displays a less-than <
character. If bytes were written after the buffer, the column contains a greater-than >
character. Use the popup menu below the list to sort the list contents.
MallocDebug helps you catch some types of problems by writing certain hexadecimal patterns into the hex values displayed in the Memory Viewer Panel window. It overwrites freed memory with 0X55
and it guards against writing beyond a block’s boundaries by putting the values 0xDEADBEEF
and 0xBEEFDEAD
, respectively, at the beginning and end of each allocated buffer.
The memory buffer inspector can be particularly helpful for determining why an object is leaking. For example, if a string is being leaked, the text of the string might indicate where it was created. If an event structure is leaked, you might be able to identify the type of event from the contents of memory and thus find the corresponding event-handling code responsible for the leak.
Some of the reports that MallocDebug presents identify obvious problems that you should fix immediately. Some of these problems include leaks, buffer overruns, and references to freed memory. Other problems are more subjective in nature and require you to make a determination as to whether there is a problem.
To improve your program’s overall allocation behavior, use MallocDebug’s detailed accounting of memory usage to explore the memory usage of your program. This can help you identify wasted memory allocations or unexpected allocation patterns and thus optimize your program’s memory usage. As you analyze your memory allocations, consider the following items:
Don’t ignore small buffers. A small leaked buffer might itself contain references to larger buffers, which then also become leaks. (The leaks
tool is better at reporting leaks of this nature.)
Look at allocation patterns during specific intervals of typical program use, especially where you suspect memory usage might be a problem.
The inverted display mode for the call stack browser can sometimes yield results faster because it shows which routines are actually calling malloc
. The normal display mode is better for seeing memory allocations in particular modules of your code.
Keep track of important statistics, such as private memory usage and total allocated memory, so you can compare them against previous measurements.
You can use MallocDebug to gather data for applications running in Mac OS X only. The following section describes some of the other issues you may run into when running MallocDebug.
MallocDebug shows the current amount of allocated memory at a given point in a program’s execution; it does not show the total amount of memory allocated by the program during its entire span of execution. Memory that has been freed is not shown.
To see memory that your program has allocated and freed, use the malloc_history
tool. See “Tracking Memory Allocations With malloc_history” for more information.
If a program crashes under MallocDebug, a diagnostic message is printed to the console that explains why the program crashed. Listing 1 gives an example of MallocDebug’s crash diagnostic message.
Listing 1 Diagnostic output from crashing under MallocDebug
MallocDebug: Target application attempted to read address 0x55555555, which can’t be read. |
MallocDebug: MallocDebug trashes freed memory with the value 0x55, |
MallocDebug: strongly suggesting the application or a library is referencing |
MallocDebug: memory it already freed. |
MallocDebug: MallocDebug can’t do anything about this, so the app’s just going to have to be terminated. |
MallocDebug: libMallocDebug cannot help the application recover from this error, |
MallocDebug: so we’ll just have to shut down the application. |
MallocDebug: ************************************************* |
MallocDebug: THIS IS A BUG IN THE PROGRAM BEING RUN UNDER MALLOC DEBUG, |
MallocDebug: NOT A BUG IN MALLOC DEBUG! |
MallocDebug: ************************************************* |
Usually a crash results from subtle memory problems, such as referencing freed memory or dereferencing pointers found outside an allocated buffer. Check suspected buffers of memory with the memory-buffer inspector (see “Analyzing Raw Memory”). If your program is referencing memory at 0x55555555
, then it is referencing freed memory.
Important: You should always investigate and fix bugs that cause your program to crash. Subtle problems may indicate a design flaw that could cost more time to fix later.
For security reasons, the operating system does not allow programs running setuid
(set the user id at execution) or setgid
(set the group id at execution) to load new libraries, such as the heap debugging library used by MallocDebug. As a result, MallocDebug cannot display information about these programs unless they are run by the target user or by a member of the target group.
If you want to examine a setuid
or setgid
program with MallocDebug, you have two options:
Use MallocDebug on a copy of the program without the setuid
or setgid
permissions set. This approach may not work if the permissions are needed to access files normally not accessible by you.
Run MallocDebug while logged in as the user who owns the file, or use the su
tool to log in as another user. Note that you must run your program by calling the executable file directly in the latter case since the open
tool runs the program as if it was launched by the user who logged in.
If you’re writing a simple program that runs from the command-line, you may need to statically link the malloc
routines into your executable before MallocDebug can attach to your program. Most programs link to the System framework, which is instrumented for use by MallocDebug. If your program does not use this framework, you can explicitly link your program with the /usr/lib/libMallocDebug.a
library. (If you are running in Mac OS X 10.3.9 or later, you can also execute the command set env DYLD_INSERT_LIBRARIES /usr/lib/libMallocDebug.A.dylib
from the debugger console to attach your program to libMallocDebug
.) You should not notice any difference in your program’s allocation behavior when linking with this library.
If you do link your application to libMallocDebug
, you should be aware of the following caveats:
If your code runs in versions of Mac OS X prior to 10.4, you must need to set the DYLD_FORCE_FLAT_NAMESPACE
environment variable to force the linker to use the malloc routines in libMallocDebug
. If you are running in Mac OS X v10.4 or later, you do not need to set this variable.
When running your program in Mac OS X v10.4 or later under gdb with libMallocDebug
installed, your program automatically drops into the debugger when libMallocDebug
detects that memory has been corrupted by a malloc
or free
call. To continue running your program, you need to jump over the affected instruction without executing it. You can do this using the jump
command in GDB.
If your program runs on a version of Mac OS X prior to version 10.3.9, you may need to execute the command " set start-with-shell 0
" in gdb to debug your program with libMallocDebug
. .
In Mac OS X v10.4 and later, child processes created by a fork
and exec
now properly inherit the DYLD_INSERT_LIBRARIES
environment variable setting. Thus, if the parent is running under libMallocDebug
, so will the child.
MallocDebug does not contain any built-in mechanism for setting environment variables. You can work around this limitation by setting your environment variables from Terminal and then launching MallocDebug from there. When launched in this manner, your application inherits the Terminal environment, including any environment variables.
Do not launch MallocDebug from Terminal using the open
command. Instead, run the MallocDebug executable directly. The executable is located in the MallocDebug.app
application bundle, usually in the MallocDebug.app/Contents/MacOS
directory.
In Mac OS X, the malloc_history
tool displays backtrace data showing exactly where your program made calls to the malloc
and free
functions. If you specify an address when calling malloc_history
, the tool tracks memory allocations occurring at that address only. If you specify the -all_by_size
or -all_by_count
options, the tool displays all allocations, grouping frequent allocations together.
Before using the malloc_history
tool on your program, you must first enable the malloc library logging features by setting the MallocStackLogging
to 1
. You may also want to set the MallocStackLoggingNoCompact
environment variable to retain information about freed blocks. For more information on these variables, see “Enabling the Malloc Debugging Features.”
The malloc_history
tool is best used in situations where you need to find the previous owner of a block of memory. If you determine that a particular data is somehow becoming corrupted, you can put checks into your code to print the address of the block when the corruption occurs. You can then use malloc_history
to find out who owns the block and identify any stale pointers.
The malloc_history
tool is also suited for situations where Sampler or MallocDebug cannot be used. For example, you might use this tool from a remote computer or in situations where you want a minimal impact on the behavior of your program.
For more information on using the malloc_history
tool, see malloc_history
man page.
In Mac OS X, the heap
command-line tool displays a snapshot of the memory allocated by the malloc library and located in the address space of a specified process. For Cocoa applications, this tool identifies Objective-C objects by name. For both memory blocks and objects, the tool organizes the information by heap, showing all items in the same heap together.
The heap
tool provides much of the same information as the ObjectAlloc instrument, but does so in a much less intrusive manner. You can use this tool from a remote session or in situations where the use of Instruments might slow the system down enough to affect the resulting output.
For more information about using the heap
tool, see heap(1)
man page.
Last updated: 2008-07-02