Measuring Your App’s Memory Usage with Instruments
On Stack Overflow I see many questions from people who have trouble interpreting the data Instruments generates. After reading this article you’ll know how to interpret the data Instruments generates for the Allocations and Leaks instruments and learn tips you can use with other instruments.
What is Instruments?
Instruments is an app that is bundled with Xcode to record and display statistics about your app as it runs. Instruments comes with a collection of instruments, each of which records a specific set of statistics. Some popular instruments include the following:
- The Leaks instrument checks your app for memory leaks.
- The Allocations instrument records statistics about memory allocations.
- The Time Profiler instrument measures CPU usage and helps you find the slow spots in your code.
If you’re learning iOS or Mac development, you’re not going to use Instruments at first. You normally use Instruments when you get closer to shipping an app. But if your iOS app is generating memory warnings when you run it or your app runs really slow, you’ll want to profile the app with Instruments.
Launching Instruments
Because Instruments is located inside Xcode’s app bundle, you won’t be able to find it in the Finder. The easiest way to launch Instruments is to profile your project by choosing Product > Profile in Xcode. When Instruments launches, you will be asked to choose a template.
Select the Leaks template and click the Choose button. The Leaks template include both the Leaks and Allocations instruments.
The Trace Document Window
When you click the Choose button, an empty trace document window opens in Instruments.
Running from top to bottom, the main areas of the trace document window are the following:
- Toolbar
- Instrument list
- Jump bar
- Detail view
- Extended detail view
- Bottom bar
For this article the only toolbar items you need to worry about are the buttons to start/stop recording and pause recording on the left side of the toolbar. The toolbar also has controls to choose an app to profile, add an instrument to the trace, and show/hide different parts of the trace document window.
The instrument list shows the instruments being used in the trace along with a graph for each instrument. The graph is going to be empty until you start recording.
The jump bar allows you to choose what to show in the detail view. The detail view shows the profiling statistics. The extended detail view shows additional statistics. For most instruments the extended detail view shows the call stack.
The bottom bar contains controls to filter the information that appears in the detail view.
Configuring an Instrument (Optional)
You won’t need to configure any instruments to follow along with this tutorial. But when you use Instruments, you may need to configure an instrument, and it helps to configure the instrument before you start recording.
Choose File > Recording Options to configure an instrument. The following screenshot shows the configuration options for the Allocations instrument:
Profiling Your App
At this point you can start profiling your app. Click the Record button on the left side of the toolbar. Your app will launch. Use your app. When you are finished, you can either click the Pause button or the Stop button. The Leaks instrument is set to check for leaks every 10 seconds so you should run your app for at least 10 seconds.
If you want to save your trace so you can look at the results later, choose File > Save.
Reading the Profiling Data
Now comes the hard part: making sense of all the data that Instruments generates. Let’s start with the graph. If you move the mouse cursor inside the graph, Instruments shows a tooltip with a piece of data about the graph. The following image shows the memory usage for the Allocations instrument:
You can focus on a specific time interval in the graph by setting an inspection range. Click inside an instrument’s graph and drag to create the inspection range. The inspection range is shaded blue, which you can see in the following screenshot:
When you set an inspection range, the statistics in the detail view change to reflect the time interval in the inspection range. Clicking in the graph outside of the inspection range you set clears the inspection range.
Allocations Instrument Results
Now it’s time to look at data for specific instruments. Let’s start with the Allocations instrument. If you select the Allocations instrument from the instrument list, the detail view shows a summary of the memory allocation data. The most important data is at the top.
To determine how much memory your app is currently using, look at the Persistent Bytes column for the category All Heap Allocations. The text is a little small in the screenshot, but it says the app is using 9.64 MB of memory, which is good for an iOS or Mac app. The Allocations instrument does not record OpenGL/ES or Metal texture memory. If your app allocates texture memory, your actual memory use will be higher than what Instruments reports. You may also want to click the checkbox in the Graph column to show the heap allocation totals in the graph.
The Allocations instrument has the following columns of data for each memory category:
- Persistent Bytes, which is the amount of allocated memory that hasn’t been freed yet.
- # Persistent, which is the number of memory allocations that haven’t been freed yet.
- # Transient, which is the total number of memory allocations that have been freed.
- Total Bytes, which is the total amount of allocated memory, including freed memory.
- # Total, which is the total number of memory allocations, including freed memory. The # Total column should equal the sum of the # Persistent and # Transient columns.
Suppose your app makes 20 memory allocations of 1,000 bytes and frees the memory for 15 of the allocations. The Allocations instrument would report the following information:
Persistent Bytes: 5,000
# Persistent: 5
# Transient: 15
Total Bytes: 20,000
# Total: 20
Allocations Instrument Call Tree
If your app allocates a high amount of memory, the first thing you’ll want to know is where your code is making the big memory allocations. To find where you’re allocating memory, switch to the call tree view. In the jump bar, click on Statistics and choose Call Trees to switch to the call tree view.
When you switch to the call tree view, you should configure Instruments to make finding your code easier in the call tree view. Click the Call Tree button in the bottom bar to open a popover with a series of checkboxes.
Select the Invert Call Tree and Hide System Libraries checkboxes. Inverting the call tree brings the memory allocating functions to the top. Hiding the system libraries hides Apple’s code, leaving your code in the call tree view.
After selecting the checkboxes, the call tree view should show your code.
Option-clicking a disclosure triangle next to a function in the call tree view expands its subtree so you don’t have to be constantly clicking disclosure triangles. Option-clicking again on the expanded disclosure triangle contracts the subtree.
For each function in the call tree, the Allocations instrument shows the bytes used and the allocation count. The bytes used is represented both as an amount and a percentage of the total memory allocated. Here’s the listing from the Framesetter.init
listing:
308.86 KB 1.2% 42
The line says the Framesetter.init
function and any functions in its subtree allocated 308.86 KB of memory, which is 1.2% percent of the total allocated memory. This function and any functions in its subtree made 42 allocations.
The subtrees keep Instruments from telling you the exact amount of memory each function allocates. Inverting the call tree reduces the subtree, which makes the listing for a function in an inverted call tree the closest approximation to the amount of memory that function is allocating. If you set an inspection range in the graph, the listing reflects the amount of memory allocated in the inspection range’s time interval.
main Listing
For most Mac and iOS apps, the first listing in the call tree view is the main
function. This function is going to have a high amount of bytes used and allocations. If you don’t invert the call tree, the main
function will show 100% bytes used. That’s because main
is the starting point of your app. Every function in your app will have main
in its call stack so every memory allocation your app makes will have main
in its call stack.
When trying to figure out where your app allocates memory, don’t worry about the main
listing. The main
function doesn’t do much in most Mac and iOS apps. There isn’t much you can do in the main
function to reduce your app’s memory usage.
Source View
Double-clicking a function in the call tree view shows the source view. For the Allocations instrument, the source view shows you the lines of code that allocated the memory along with the percentage of memory allocated. The percentage is relative to the function. If a line of code says 80%, it means that line of code allocated 80% of the memory the function allocated.
Notice how each listing in the source view has an icon with an exclamation point. Clicking that icon shows the heaviest call stack for that line of code.
Leaks Instrument Results
Now it’s time to look at the Leaks instrument. The graph will tell you if you have any memory leaks in your app. A memory leak occurs when your app allocates memory and never frees the memory. The following screenshot shows an example of the graph for the Leaks instrument:
The screenshot shows six memory leak checks. For the first four checks, Leaks found no memory leaks so it leaves a green icon with a checkmark on the graph. On the fifth check, Leaks found some leaks so it leaves a red icon with an X on the graph. On the last check Leaks found no new leaks so it leaves a gray icon with a dash on the graph.
Leaks Instrument Call Tree
If Instruments finds memory leaks in your app, the next step is to find where the leaks are occurring in your code. The steps to do this are the same as for the Allocations instrument: switch to the call tree view by using the jump bar and select the Invert Call Tree and Hide System Libraries checkboxes from the Call Tree popover in the bottom bar. Selecting the checkboxes will limit the call tree listing to functions you wrote.
For each function in the call tree listing, Instruments shows the bytes used and the number of leaks. Like the Allocations instrument, the bytes used is represented both as an amount and a percentage, but in the Leaks instrument the percentage represents the percentage of total leaked memory. Here’s the listing for the malloc
function in the screenshot.
19.12 KB 98.0% 92
The listing says the malloc
function has 92 memory leaks totaling 19.12 KB. The leaks totaled 98% of the leaked memory in the app.
Double-click any functions in your call tree view to see where the leaked memory is allocated.
The fact that a function allocates leaked memory doesn’t necessarily mean the function is responsible for leaking the memory. But it’s a good place to start looking for the source of the leak.
Troubleshooting
This section contains fixes for common issues people run into with Instruments.
The Call Tree View Shows Memory Addresses
A problem many people run into when using Instruments is the call tree view shows memory addresses instead of the names of functions. The usual cause of this problem is Instruments being unable to locate the dSYM file that contains the project’s debugging symbols. The function names are in the debugging symbols.
To locate the dSYM file, choose File > Symbols in Instruments. A sheet like the following will open:
Select your app from the left side of the sheet. Click the Locate button to find the dSYM file.
The dSYM file is usually located in the same directory as the app bundle of the release version of your project. The app bundle is in the Derived Data folder. You can check the location of your Derived Data folder in Xcode by choosing File > Project Settings, but the Derived Data folder is usually in the following folder:
/Users/YourUsername/Library/Developer/Xcode/DerivedData
There will be a folder for your project in the Derived Data folder with a long string of random characters after the project name. The release version of the project is in the following folder:
.../DerivedData/ProjectName/Build/Products/Release
An iOS project will have the name Release-iphoneos
instead of Release
.
The Source View Doesn’t Show Source Code
You double-click a function in the call tree view, expecting to see that function’s code in the source view. But instead you see either assembly language code or a blank view that says Unavailable.
The usual cause of this issue is double-clicking a function you didn’t write. Examples of functions you didn’t write include functions from Apple’s frameworks and default initializers in your structs and classes. Make sure you double-click a function you wrote.
Summary
I want to summarize the most important points to take away from this article. These points apply to most instruments, not just the Leaks and Allocations instruments.
- Choose Product > Profile in Xcode to profile your app.
- Use the call tree view to find problem areas in your code.
- Select the Invert Call Tree and Hide System Libraries checkboxes to find your code in the call tree view.
- Option-click the disclosure triangles in the call tree view to minimize the amount of clicking you have to do.
- Double-click a function in the call tree view to see statistics on specific lines of code.
- Don’t worry about the listing for the
main
function in the call tree view. You have no control overmain
.