ios memory leak hunting - ios

I work on an application that comprises several minigames. One of these minigames obviously leaks memory. This minigame, in turn, is not that "mini". It includes a dozen of sources. And frankly speaking, they are awful.
How do I find memory leak? My idea is to set some "begin hunt" marker when the minigame starts and output all objects that were allocated since marker set and not freed at the moment when minigame finishes. Do iOS debug facilities allow it?

Related

Free up memory when entering background

I have a Dictionary of SKTextures that caches recently loaded textures (70 or so). I'm releasing them when a memory warning is received, like this:
let keys = textures.keys.array
for key in keys {
textures[key] = nil
}
This works great, and I can see in the Xcode debugger that the memory is actually being freed.
When I try to release them on applicationDidEnterBackground(), however, it doesn't look like it fully works. The memory usage drops by a small inconsistent amount (5-20 MB), but the rest of it doesn't get freed until the app is resumed.
Is this a quirk with the debugger and the memory is being freed? Or does the garbage collector not have enough time to clean up before the app is suspended? Or am I doing this all wrong?
Update: I'm pretty sure it's related to SpriteKit automatically pausing the SKView when entering the background. This is easily reproducible by pausing the view manually just before releasing the textures. Then the memory won't be freed even when the memory warning is received.
Is this a quirk with the debugger and the memory is being freed
I would describe it a quirk of how memory management works. There's a virtual memory management system. The memory is being freed but that doesn't mean that it is emptied / reclaimed, if you see what I mean. What you've done is to say that this memory is free, but the system won't take it unless it needs it.
The way to test is to write another app that deliberately gobbles memory, and run it while your app is in the background. Instruments will work even when you are suspended, and will show that in fact your memory usage goes down.

Instruments and heap growth, when is growth really a leak?

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:
What does this type of behavior indicate? Do I have memory issues? Is there some type of lazy release of memory somewhere?
In a sea of iterations that show persistent memory, what does one zero heap growth iteration mean?
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?
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?
If I have something like a retain cycle - wouldn't the persistent growth be consistent?
Any answers to insights would be extremely helpful!
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.

Using Instruments to Work Through Low Memory Warnings

I am trying to work through some low memory conditions using instruments. I can watch memory consumption in the Physical Memory Free monitor drop down to a couple of MB, even though Allocations shows that All Allocations is about 3 MB and Overall Bytes is 34 MB.
I have started to experience crashing since I moved some operations to a separate thread with an NSOperationQueue. But I wasn't using instruments before the change. Nevertheless, I'm betting I did something that I can undo to stop the crashes.
By the way, it is much more stable without instruments or the debugger connected.
I have the leaks down to almost none (maybe a hundred bytes max before a crash).
When I look at Allocations, I only see very primitive objects. And the total memory reported by it is also very low. So I cant see how my app is causing these low memory warnings.
When I look at Heap Shots from the start up, I don't see more than about 3 MB there, between the baseline and the sum of all the heap growth values.
What should I be looking at to find where the problem is? Can I isolate it to one of my view controller instances, for example? Or to one of my other instances?
What I have done:
I powered the device off and back on, and this made a significant improvement. Instruments is not reporting a low memory warning. Also, I noticed that Physical Free Memory at start up was only about 7 MB before restarting, and its about 60 MB after restarting.
However, I am seeing a very regular (periodic) drop in Physical Free Memory, dropping from 43 MB to 6 MB (an then back up to 43 MB). I would like to knwo what it causing that. I don't have any timers running in this app. (I do have some performSelector:afterDelay:, but those aren't active during these tests.)
I am not using ARC.
The allocations and the leaks instruments only show what the objects actually take, but not what their underlaying non-object structures (the backing stores) are taking. For example, for UIImages it will show you have a few allocated bytes. This is because a UIImage object only takes those bytes, but the CGImageRef that actually contains the image data is not an object, and it is not taken into account in these instruments.
If you are not doing it already, try running the VM Tracker at the same time you run the allocations instrument. It will give you an idea of the type memory that is being allocated. For iOS the "Dirty Memory", shown by this instrument, is what normally triggers the memory warnings. Dirty memory is memory that cannot be automatically discarded by the VM system. If you see lots of CGImages, images might be your problem.
Another important concept is abandoned memory. This is memory that was allocated, it is still referenced somewhere (and as such not a leak), but not used. An example of this type of memory is a cache of some sort, which is not freeing up upon memory warning. A way to find this out is to use the heap shot analysis. Press the "Mark Heap" button of the allocations instrument, do some operation, return to the previous point in the app and press "Mark Heap" again. The second heap shot should show you what new objects have been allocated between those two moments, and might shed some light on the mystery. You could also repeat the operation simulating a memory warning to see if that behaviour changes.
Finally, I recommend you to read this article, which explains how all this works: http://liam.flookes.com/wp/2012/05/03/finding-ios-memory/.
The difference between physical memory from VM Tracker and allocated memory from "Allocations" is due to the major differences of how these instruments work:
Allocations traces what your app does by installing a tap in the functions that allocate memory (malloc, NSAllocateObject, ...). This method yields very precise information about each allocation, like position in code (stack), amount, time, type. The downside is that if you don't trace every function (like vm_allocate) that somehow allocates memory, you lose this information.
VM Tracker samples the state of the system's virtual memory in regular intervals. This is a much less precise method, as it just gives you an overall view of the current state. It operates at a low frequency (usually something like every three seconds) and you get no idea of how this state was reached.
A known culprit of invisible allocations is CoreGraphics: It uses a lot of memory when decompressing images, drawing bitmap contexts and the like. This memory is usually invisible in the Allocations instrument. So if your app handles a lot of images it is likely that you see a big difference between the amount of physical memory and the overall allocated size.
Spikes in physical memory might result from big images being decompressed, downsized and then only used in screen resolution in some view's or layer's contents. All this might happen automatically in UIKit without your code being involved.
I have the leaks down to almost none (maybe a hundred bytes max before a crash).
In my experience, also very small leaks are "dangerous" sign. In fact, I have never seen a leak larger than 4K, and leaks I usually see are a couple hundreds of bytes. Still, they usually "hide" behind themselves a much larger memory which is lost.
So, my first suggestion is: get rid of those leaks, even though they seem small and insignificant -- they are not.
I have started to experience crashing since I moved some operations to a separate thread with an NSOperationQueue.
Is there a chance that the operation you moved to the thread is the responsible for the pulsing peak? Could it be spawned more than once at a time?
As to the peaks, I see two ways you can go about them:
use the Time Profiler in Instruments and try to understand what code is executing while you see the peak rising;
selectively comment out portions of your code (I mean: entire parts of your app -- e.g., replace a "real" controller with a basic/empty UIViewController, etc) and see if you can identify the culprit this way.
I have never seen such a pulsating behaviour, so I assume it depends on your app or on your device. Have you tried with a different device? What happens in the simulator (do you see the peak)?
When I'm reading your text, I have the impression that you might have some hidden leaks. I could be wrong but, are you 100% sure that you have check all leaks?
I remember one particular project I was doing few month ago, I had the same kind of issue, and no leaks in Instruments. My memory kept growing up and I get memory warnings... I start to log on some important dealloc method. And I've seen that some objects, subviews (UIView) were "leaking". But they were not seen by Instruments because they were still attached to a main view.
Hope this was helpful.
In the Allocations Instrument make sure you have "Only Track Active Allocations" checked. See Image Below. I think this makes it easier to see what is actually happening.
Have you run Analyze on the project? If there's any analyze warnings, fix them first.
Are you using any CoreFoundation stuff? Some of the CF methods have ... strange ... interactions with the ObjC runtime and mem management (they shouldn't do, AFAICS, but I've seen some odd behaviour with the low-level image and AV manipulations where it seems like mem is being used outside the core app process - maybe the OS calls being used by Apple?)
... NB: there have also, in previous versions of iOS, been a few mem-leaks inside Apple's CF methods. IIRC the last of those was fixed in iOS 5.0.
(StackOVerflow's parser sucks: I typed "3" not "1") Are you doing something with a large number of / large-sized CALayer instances (or UIView's with CG* methods, e.g. a custom drawRect method in a UIView?)
... NB: I have seen the exact behaviour you describe caused by 2 and 3 above, either in the CF libraries, or in the Apple windowing system when it tries to work with image data that was originally generated inside CF libraries - or which found its way into CALayers.
It seems that Instruments DOES NOT CORRECTLY TRACK memory usage inside the CA / CG system; this area is a bit complex since Apple is shuffling back and forth between CPU and GPU ram, but it's disappointing that the mem usage seems to simply "disappear" when it clearly is still being used!
Final thought (4. -- but SO won't let me type that) - are you using the invisible RHS of Instruments?
Apple hardcoded Instruments to always disable itself everytime you run it (so you have to keep manually opening it). This is stupid, since some of the core information only exists in the RHS bar. But I've worked with several people who didn't even know it existed :)

iOS -- Using and Understanding Instruments using Allocations and Memory Monitor (Physical Memory Free)

I'm in the process of understanding how to put instruments to better use. I just finished a leak management exercise, and instruments is reporting very few leaks. I'll figure those out later. In the mean time, my app is crashing, and it appears that its related to memory pressure.
So I looked at this in Instruments. I have Allocations and Memory Monitor in use. Allocations shows a pretty steady 3 to 4 MB Live bytes while I just let my app initialize and come to equilibrium. Overall bytes, however, jumps to over 50 MB. I didn't think much of this until I looked at the Memory Monitor and I see that memory usage goes up and down, causing memory warnings. (It seems strange to me that this doesn't show up on the allocations graph at the same time.)
The app should be at an equilibrium point, but apparently it's not. My question is how can I use instruments to help me understand why memory usages is rising and falling?
Instruments as a tool for debugging is simply excellent. From what I can understand, you have been trying to use the allocations tool, so I'll go over that. Allocations details the number of objects your application allocates during it's execution, along with their in-memory references, locations, even the calling code that allocates said objects. When instruments starts running the allocations tool, your application begins reporting all allocations as blue dots, which pile up higher and higher as your application executes (naturally, as you should be allocating more and more objects). Overall Bytes displays the amount of memory EVERY allocation your app has made added together. I want to stress this for your case: it does not mean your app is currently using 50 mb of memory!, it just means that your app has used 50 mb total. Your app is obviously limited to the amount of memory the device has, and 3-4 mb is not a lot when you consider that the first gen. iPhone had about 128mb, but for more complicated applications, the OS will usually kill off other applications before it kills yours.
As for the other allocation graph with spikes, rather than a continuous line graph, that is to detail the number of allocations going on at that point in time. Usually, the spikes can be ignored, unless there are a lot of large spikes in one small amount of time.
Anyways, to address your specific memory warning problem, it honestly depends how many memory warnings you are receiving, and at what level the warning are at. And as for your leaks, my only word of advice is: Squash them as soon as possible! When you see a leak (a red bar in the leaks tool), click on the bar and find the objects that are being leaked. When you select a leaked object, then select the right sidebar, it will show you the code that is leaking. When you double click on any part of the right sidebar, it'll even open up the specific line and class the leak originated from!

Does FastMM detect all memory leaks

Somebody suggested recently ( My program never releases the memory back. Why? ) that my program leaks some memory. I have FastMM set to aggressive and it reports no memory leaks when I shutdown the program.
Anyway, I would like to know if there can be memory leaks that are no detected by FastMM?
Update: I don't personally use Win API to allocate memory. But I am afraid that some 3rd party components I use (not so many) may use it. Can you let me know all possible API calls that FastMM cannot intercept? I will do a search in my code for them. Thanks.
Delphi 7, Win 7 32 bit
FastMM 4.97
I am not interested about interfaces.
FastMM is a layer on top of Windows memory management. Obviously, if you (or some component or whatever) uses Windows APIs to allocate memory, then such allocation bypasses FastMM and you won't be able to track it. BTW Delphi memory managers themselves use that APIs to allocate chunks of memory. So if you need to see allocations on that level, FastMM is not enough - you must use tools like AQTime and similar (as I suggested in the previous question).
No, only memory leaks which memory was alocated by FastMM.
EDIT:
Maybe the answer looks wrapped but it is not! If anyone check the FastMM haw is made than can see that every pointer of memory alocation is pushed (and poped out at FreeMem) in to one of stacks (there is more stacks, depend of memory size) so at the end of closing application the FastMM only check stacks, if something in stacks, and if it is, than report memory leak!
I've never known FastMM to fail to detect a memory leak.
There are several possible causes: (which apply to any memory manager)
your main program loop leaks memory, but does so to something that is freed on shutdown
the simplest case is logging to a memo. The memo gets bigger and bigger, but is destroyed on shutdown.
the memory is allocated outside fastmm's control
directly allocating from windows
memory allocated in dlls etc.
Heapfragmentation. A memory manager keeps large blocks allocated (e.g. because it still contains a small % of allocations). Result: The app doesn't use it, but it is not release to the OS either. The runtime/memorymanager keeps it around.
fastmm should be more resilient against this phenomena, but in doubt try to print heapmanager info to see if this is the case.
There is a lot of good answer already, but one point that wasn't mentionned yet...
There is a lot of "leaks" that won't get detected by most memory leak detector since the memory IS freed, but way after it isn't used anymore. For exemple, object stacked in a TObjectList. Object are put in the object list, but after you are done using them, you don't free them. They will be destroyed when the object list is destroyed too (When application close for exemple, assuming OwnsObject=True). Since the objects are actually freed, objects are not "leaked", but still make your application use more and more memory over time.
FastMM won't report those as it only makes "full run" analysis. To detect those, you need a memory leak detector that allows to do partial runs, that is, analyzing what "leaked" between point A and point B during the execution. AQTime that Eugene mentionned allow that kind of checks. But be aware that is takes a bit of analysis because that will yield many false-positive (Pretty much all "realloc" operations will be marked as a leak).
FastMM does not detect leaks of memory allocations not going through FastMM.
This can include GlobalAlloc calls from 3rd party libraries or DLLs you use.
Edit: Microsoft's MSDN has a nice list of memory allocation methods.
This was in fact the problem I mentioned I mentioned in my answer to your previous FastMM question.
You can use a tool like VMMap to track the memory leaks that FastMM cannot detect.
--jeroen

Resources