9

I'm using instruments on a device to try to figure out if I have any memory leaking or abandoned. Specifically I am using leaks and allocations. While instruments doesn't point out any leaks, that doesn't mean I don't have memory issues. I've been working on this for weeks, and I don't seem to be any closer to figuring out what issues I have (ugh).

I am testing a particular action by taking a heapshot after the action and repeating. After the first few "settling" generations, I can see that the growth and persistent count all start out at a certain number (several kb). After many repeated iterations (say 10-20), some (not all) slowly end up draining to 0. It takes a while, but it does happen. The generations where there remains persistent memory never actually show me anything that I find helpful, as the stack trace show all system libraries.

So my questions are:

  1. What does this type of behavior indicate? Do I have memory issues? Is there some type of lazy release of memory somewhere?
  2. In a sea of iterations that show persistent memory, what does one zero heap growth iteration mean?
  3. If the stack trace for a particular generation points only to system libraries, does this mean the heap growth for that generation is valid or that there is a bug? Or could it still mean that there is something holding onto the memory on my end?
  4. What does it mean when the stack trace shows your library and method, but it is greyed out like the system code and has a little house icon, vs a a line with your library and method that is in black and has a little person icon?
  5. If I have something like a retain cycle - wouldn't the persistent growth be consistent?

Any answers to insights would be extremely helpful!

dragonflyesque
  • 485
  • 5
  • 11
  • Did you figure this out? I'm seeing the same thing when allocating lots of new controllers. – Paul Sturgess Jan 15 '15 at 12:56
  • 1
    After struggling for some time, I figured that even one zero heap growth after 20-30 iterations was a sign that something was right and moved on. All of the allocations I came upon were from system libraries, and the memory that was accruing was not very much. Not a good answer, I know. If someone else can chime in that would be great. – dragonflyesque Jan 16 '15 at 04:35
  • You might have a tight loop somewhere where things don't get freed fast enough. Wrap that tight loop in @autorelease – uchuugaka Jan 16 '15 at 10:55
  • @uchuugaka isn't the main autorelease pool of application supposed to drain at the end of every event loop. So that should drop the heap size after every event loop ends – salman140 Jan 22 '15 at 09:22
  • @dragonflyesque are you using a lot of immutable objects implemented as flyweights like NSNumbers? Could be the reason since these objects are shared across the application and are not deallocated until all the references to them are removed. So you could actually have a look at the type of objects that are taking most memory – salman140 Jan 22 '15 at 09:27
  • Not exactly that way. It is perfect for blocks of code like loops that have lots of temporary objects. See this https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html – uchuugaka Jan 22 '15 at 10:27
  • @dragonflyesque check https://developer.apple.com/videos/wwdc/2013/?id=410 to make things clear – Mykola Denysyuk Jan 30 '15 at 08:28
  • @salman140 good point. I do have several, but not a ton of immutable objects that are autoreleased. I will try to change them to see if that helps. – dragonflyesque Feb 03 '15 at 13:51

1 Answers1

1

I'll take a stab at your questions:

What does this type of behavior indicate? Do I have memory issues? Is there some type of lazy release of memory somewhere?

Since you can't know how the system frameworks manage their private memory needs, you must assume that yes, there could be lazy/deferred release of memory happening any time you call into the system frameworks, which in most apps is "all the time". Beyond not being able to rule it out, I can say with some certainty that there definitely are long-lived allocations triggered by seemingly-innocuous system framework usage. (See the discussion of UIWebView's long-lived memory use in this answer for an example.)

In a sea of iterations that show persistent memory, what does one zero heap growth iteration mean?

Hard to say. A good first-order guess might be that the heap growth associated with the iteration was somehow exactly offset by a lazy/deferred release of the memory allocated for a previous iteration.

If the stack trace for a particular generation points only to system libraries, does this mean the heap growth for that generation is valid or that there is a bug? Or could it still mean that there is something holding onto the memory on my end?

If Instruments shows heap growth, then that heap growth almost certainly exists. Whether that heap growth is something you have direct control over depends. If you make no calls into system frameworks (not likely), then it's definitely your fault. Once you make a call into the system frameworks, you have to accept the possibility that the framework might allocate memory that stays allocated after your call returns.

What does it mean when the stack trace shows your library and method, but it is greyed out like the system code and has a little house icon, vs a a line with your library and method that is in black and has a little person icon?

Lines being greyed out indicates that Instruments doesn't have debug symbols for that line. That's all. It doesn't indicate anything specific with regard to memory use.

If I have something like a retain cycle - wouldn't the persistent growth be consistent?

If each iteration created a new object graph with cyclic retains, then yes, you would expect that each iteration would cause heap growth by at least the size of that object graph. That said, small object graphs can easily be lost in the "noise." If you have suspicions, one way is to have objects of a "suspect" class perform a huge allocation that will make them stand out from the "noise." For instance, make your object malloc a megabyte (or more) for every instance (and, obviously, free it when the instance is deallocated.) This can help problem areas stick out where they might not have originally.

Community
  • 1
  • 1
ipmcc
  • 29,581
  • 5
  • 84
  • 147