Finding Abandoned Memory
Detect abandoned and unused memory allocated by your app using the Allocations instrument. Releasing this memory increases memory efficiency, improving the quality of your app.
In the Instruments analysis tool, open the Allocations template.
From the Choose Target pop-up menu, choose your app.
Click the Record button.
Choose the Display Settings inspector in the sidebar pane.
Perform an action or short sequence in your app that you can repeat and that starts and finishes in the same state.
After one iteration of the repeatable action, click the Mark Generation button in the inspector sidebar to take a snapshot of the heap.
Repeat steps 5 and 6 several times, until you see whether or not the heap is growing without limit; then click the Stop button.
Analyze objects captured by the heap snapshot to locate abandoned memory.
The process described here for finding abandoned memory is called “generational analysis” and is based on the premise that each “generation” of objects created during a scenario should be proportional to the long-term impact of that scenario on the program’s state. If it’s opening and closing a window, for example, then the objects in that generation should eventually be destroyed. If it’s adding a new contact to an address book, then objects other than the new contact and its properties should eventually be destroyed.
There are a few types of persistent memory that are picked up by this analysis technique:
Leaked memory: Memory unreferenced by your application that cannot be used again or freed (also detectable by using the Leaks instrument).
Abandoned memory: Memory still referenced by your application that has no useful purpose.
Cached memory: Memory still referenced by your application that might be used again for better performance.
To avoid having abandoned memory, make sure the heap does not continue to grow when the same set of operations are continuously repeated. For example, actions such as opening a window and then immediately closing it, or setting a preference and then immediately unsetting it, should return the app to a previous and stable memory state. Extensive cycling through such operations should not result in unbounded heap growth.
To ensure that none of your code abandons memory, repeat user scenarios and use the Mark Generations feature after each iteration. After the first few iterations (where caches may be warmed), the persistent memory of these iterations should fall to zero. If persistent memory is still accumulating, click the focus arrow to see a call tree of the memory. This tree identifies which code paths are responsible for the allocations that are eventually being abandoned. Your code is colored black in the stack trace. Make sure that your scenarios exercise all your code that allocates memory.
If the heap continues to grow after the first few iterations of the cycle, you know your app is abandoning memory. Find a heap snapshot that seems representative of the repeated heap growth. Click the focus button to the right of the heap snapshot name to display objects created during that time range that are still living at the end of the program’s execution.
After you stop the trace, you can still take snapshots by dragging the inspection head in the trace window timeline to where you want the snapshot and clicking Mark Generations.