Just a quick post to share what I learnt while investigating crashes in my iPad app. The crash reports consistently showed low memory warnings before the crash so I started using instruments to profile the app. Here’s some tips that may help you out when you are stuck using the instruments tool:
Turn off Zombies. Zombies are great for debugging messages sent to de-allocated objects, but they come at a cost of memory – NSZombies are never deallocated. Turning this off immediately showed great reductions in terms of live bytes in the allocations instrument. Apple’s documentation advises zombies to be used only on the simulator while debugging and testing.
Mark your heap before and after performing actions on your app to inspect the memory changes introduced by the action. A simple evaluation to keep in mind is that if any set of actions returns the app to the same state as before the actions, the changes in heap before and after the set of actions should be close to 0. Any significant amounts detected here would signal a leak, which need not necessarily reflect in the leaks instrument profile. Many conditions, like circular references, may cause actual leaks to be ignored in the leaks instrument, but that’s the focus of another article.
The Allocations and Activity Monitor operates differently, and gives very different results. I made the mistake of assuming the ‘live bytes’ column in the allocations report to be what my app was consuming. While allocations reported that my app is using a consistant amount of memory, I was still receiving low memory warnings, so I finally decided to run activity monitor. Surprise – the low 6.3mb consumption in allocations actually translated to 180mb in activity monitor. Why is that so? Turns our when you deallocate memory in your app, or even if you are using ARC, the memory is only released at undetermined intervals when iOS feels like it. While allocations track every memory your app allocates and deallocates, the activity monitor gives a true reflection of your app’s memory footprint.
But of course, those latent released memory wouldn’t chalk up to 100 over mb’s. The real culprit turns out to be CoreGraphics, while it handles all on screen drawings and image operations behind the scenes. The real memory consumed by these operations are not reflected in allocations at all. ARC also does not track CG objects so do remember to release them properly.
Now knowing the real issue, I proceed to refactor and restructure my drawing code to improve reuse of bitmap contexts and reduce repainting areas by keeping track of dirty rectangles. Hope these tips would help give some ideas to those of you debugging your crashes at 3 in the morning. Do also share some of your own tips or experiences in the comments!