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.
Related
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?
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 :)
I've kind of a weird issue with my iOS app.
after a while my app goes low in memory so memory warning, everything seems to be fine, but when I check the memory usage I noticed that all the calls to viewDidUnload didn't free up lot of memory, so after a few click in my app, it goes again in memory warning, everything seems to be fine again, but not a lot a memory have been released, so it goes again in memory warning faster, and then it crash (after the third memory warning most of the time). this crash is random : app freeze, app leaves, my debugger says app paused, but no bad access or sigbort, no zombies.
my guess is that memory warning can't free up enough memory has it should.
(I checked all my viewDidUnload and make nil every objects that are allocated in viewDidLoad)
Any help will be usefull !
thanks a lot.
So I managed to work with my issue.
I wrote "-(void) dealloc" methode in all my controllers and check if I enter in it as I should. (on pop controller, dissmiss etc..)
Every time it didn't, I do step by step in the controller to see what was retaining my controller from beeing dealloc.
most of the time it was some property that was not in "unsafe_unretained"
delegate that was in "ASSIGN" (and should not be in assign but in unsafe_unretained)
(heritage from non-ARC project...)
I also had some strange controller with XIB that was not deallocated even if empty.
I rebuild new one step by step with copy/paste and finaly with exactly the same code, the new controller was released, with no visible difference between then !!! gnneee
at least I know how to debug that kind issues now...
I don't think there's any way to give a specific answer without more data so the best I can do is suggest that you stop guessing what might be happening with your app and learn how to measure what is actually going on. Run your app under Instruments and you'll be able to check for leaks and also actually see what classes are responsible for the most of your application's memory footprint.
You should make sure you know how to use both the Leaks instrument to identify leaked object but also the Allocations instrument to identify orphaned (but not leaked) sets of objects which should have been released or just cases where your app is not responding to memory warnings as you expected.
https://developer.apple.com/library/ios/#documentation/developertools/conceptual/InstrumentsUserGuide/AboutTracing/AboutTracing.html might be a good place to start and there are a number of tutorials available as well; http://www.raywenderlich.com/2696/how-to-debug-memory-leaks-with-xcode-and-instruments-tutorial and http://www.friday.com/bbum/2010/10/17/when-is-a-leak-not-a-leak-using-heapshot-analysis-to-find-undesirable-memory-growth/ are among the first results I saw.
Vassily,
First, if you aren't yourself releasing extra memory, the the -didReceiveMemory warning does you no good and the OS will keep asking for memory until you are killed. This sounds like it is your problem.
Second, if that isn't the problem then you are probably getting terminated due to the size of your resident memory partitions. Make sure you look at your VM allocation in Instruments. I expect the MALLOC_TINY or MALLOC_SMALL both have greater than 5 MB resident and dirty footprints. Due to the nature of small allocations these VM regions will never shrink. The only option you really have is to not create a lot of small items in the first place. This is really only something you can address by changing you code's algorithms to use less memory.
Andrew
Writing a program for the iphone. Realized that I forgot to release an object, but there was really no indication that the object was not released everything just worked.
What is the best way to track something like this down? Is there a way to see what objects still exist in memory when the program exits out?
Take a look at the Leaks tool in Instruments.
Strictly speaking, when the program exits, it doesn’t matter what you’ve left in memory: the system frees everything that your application allocated throughout its lifetime. Since iOS 4, though, apps usually just get frozen in the background and don’t exit until the system kills them to free up memory. To avoid that—and to reduce your app’s memory footprint, which is important while it’s running—you should, as highlycaffeinated and Daniel suggested, use Instruments’s Leaks tool to check for objects that aren’t getting deallocated properly.
When the app exits, anything in memory is destroyed by the system (not deallocated-- but just outright destroyed when the address space is given back to the system).
While others have suggested using the Leaks tool to find leaks in your app, Leaks won't find many many kinds of memory accretion. If an object is allocated, shoved in a cache somewhere, then the key to that object in the cache is lost, the object is effectively leaked (can never be used again) but won't be find by Leaks because it is still connected to your viable object graph.
A better bet is to use Heapshot analysis to see how your app's object graph grows over time. I wrote up a tutorial on using Heapshot analysis that you might find useful.
If you want to grab a snapshot just before your app exits, then put a sleep(1000); into your code in either an application termination handler or somewhere else that is executed just before the app exits.
Just remember to remove it before shipping a production build. :)
Once an application quits - you don't have access to that. But Instruments (an XCode tool) can look for memory leaks.
Nothing exists in memory when pprogram exits. But you can start with analyzing your code (Product -> Analyze) and running it with (Product -> Profile) Allocations or Leaks in Instruments to find memory management issues.
I am running an ipad application compiled for release and am seing memory warnings once in a while.
When I run the app on the device and connect Instruments, I see that the app never passes 40MB of real memory, but the warnings are still occurring.
What might be causing this? How can I better track down the reason?
40 MB of real memory is a lot, for an iPad. Even if it was not, the system will deliver the low-memory warning to you from time to time anyway, without your application being the main culprit. Tracking down precise memory usage in your application is sometimes hard, I’d suggest to spend some time with the Object Allocation instrument while working with the app. If you are not getting killed and you are sure that you do not leak the memory, you can also simply ignore the warnings.
40MB is high for the iPad considering it only has 256MB to start with. There could be other applications holding on to memory which will be killed off as more memory is needed. Just make sure you aren't leaking anything. Also use NSAutoReleasePools where applicable to reduce peak memory usage in memory intensive loops.