Examining your application’s memory allocation patterns can help reveal algorithms that may not be the most efficient in their memory use. If you see a large number of allocations occurring during a loop, you may decide to go back and change the allocations to occur outside of the loop. This kind of reduction can have a significant increase in application performance.
Apple provides several tools for examining your memory usage. The MallocDebug tracks the location of memory allocations by recording the application call stack whenever a memory-related function is called. The malloc_history
tool does many of the same things as MallocDebug but from a command-line interface. You can use these tools to look for unexpectedly large allocations or allocations that were made but are no longer needed.
If you are writing a Cocoa application, you should use the ObjectAlloc program and heap
tool to see which Objective-C objects your program creates. The ObjectAlloc program is good for finding problems involving allocation trends, retain/release problems, or other problems involving object allocations. Similarly, the heap
tool helps you examine the objects currently in use by a program.
Debugging Allocations With MallocDebug
Tracking Memory Allocations With malloc_history
Observing Allocations With ObjectAlloc
Examining Heaps With the heap Tool
The MallocDebug application provides tools for inspecting your program’s memory use and for finding memory 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.
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.
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 Memory Leaks.”
After launching MallocDebug, the main window appears (Figure 1). 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 1). This browser shows you where memory allocations occurred by gathering stack snapshots whenever one of the malloc library routines was encountered. Figure 2 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 3 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 4 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 1) 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.
The following section describes some of the 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 Terminal 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, execute the command " set $PC+=4
" in gdb then continue.
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.
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 malloc_history
, see the man pages.
ObjectAlloc allows you to observe memory allocation activity in an application. It shows you the allocations in terms of the number of objects created, rather than where the allocations occurred. Its real-time histograms allow you to directly perceive changes and trends in object counts. It also retains a history of allocations and deallocations, allowing you to identify overall allocation trends.
The information displayed by ObjectAlloc 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 are recorded.
When you first launch ObjectAlloc, it asks you to choose the application you want to inspect. After selecting the application, ObjectAlloc displays the main window so that you can launch the application and start gathering data. The main window contains several buttons for controlling the launch and execution of the application, as well as for setting mark points from which to examine data. Figure 5 shows the ObjectAlloc main window.
When you click the Start button, ObjectAlloc launches the application and starts displaying object allocation data as it occurs. You can use the buttons along the top of the window to control the gathering and display of collection data. If you want to view past allocations, you must pause or stop the data gathering process before using the controls to step forward or backward through the allocation history. You can use the slider control just under the buttons to watch changes in allocation counts over time.
The Mark button sets a starting point from which to watch allocations. This starting point is a convenience that lets you view allocations over a more recent period of time. Use the “Show since mark” checkbox to toggle between displaying events that occurred since the mark was set and displaying events that occurred since the application was launched.
Note: Due to the sheer quantity of information being processed, updating the ObjectAlloc main window can slow the system down noticeably. Uncheck the “Live update” checkbox to update the display only when the ObjectAlloc window is activated or deactivated.
The Global Allocations tab contains a table with a listing of all memory blocks ever allocated in the application. 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 Current column shows the number of blocks of each type allocated but not (yet) released. The Peak column shows the largest number of blocks of each type that existed at any given time. The Total column shows the total number of blocks of each type that have been allocated, including blocks that have since been released.
The histogram bars to the right of the Total column are graphical representations of the three columns: the dark portion of the bar indicates the Current value, the middle portion of the bar is the additional number under Peak, and the complete length of the bar indicates the value under Total. The Scale slider controls the number of objects represented by each pixel in a bar (the actual number is shown to the right of the bar).
The “Counts are bytes” checkbox changes the numbers in the Current, Peak, and Total columns to reflect the number of bytes allocated (per object type) instead of the number of objects allocated (per object type).
The Instance Browser tab lists each type of block. Clicking a block type displays a list of all instances of that block. Clicking the address of a block instance displays a list of all allocation events pertaining to that block, including allocation, retain, release, autorelease, and free events. If the block has not yet been freed, the contents of the block are displayed in the bottom pane of the ObjectAlloc window. Clicking an event brings up a textual description of the event, including a function call stack.
The Call Stacks tab displays a table of each block type along with the number of instances (Count) and the number of bytes allocated to those instances (Size). The furthest-right column of this table contains the first item of a hierarchical function call tree. Clicking the disclosure triangle displays the next level of the function call stack. When the function call stack is open, it displays the location of each allocation.
The Descend Unique Path button discloses the selected function call stack to the deepest function shared by each instance’s function call stack. The Descend Max Path button discloses the selected function call stack to the deepest function in the stack.
Here are some general guidelines for interpreting the data reported by ObjectAlloc:
Does the number of instantiated objects match your expectations? If not, you might be creating more temporary objects than anticipated.
Examine the relationships between different objects. If you allocate one custom object, did you expect to see several other objects created along with it?
Examine the colored bars in the allocation graph. Large differences between the peak and current allocations (or between the total and current allocations) indicate a surge in allocations. This could reflect a large number of autoreleased objects needing to be released earlier from a loop.
The heap
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 application, 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 ObjectAlloc might slow the system down enough to affect the resulting output.
© 2003, 2006 Apple Computer, Inc. All Rights Reserved. (Last updated: 2006-06-28)