I have done my best to make sure that views are released when they are no longer needed and when pushing the app and then looking at the Debug Memory Graph I see that no objects are present that shouldn't be there. However when I look at the memory graph I can see that although the graph does sometimes bump back down, it still slowly trends upwards. An example is attached.
I am new to building large projects in iOS and am mostly asking is this normal? Is it simply impossible to free all used memory with ARC? Or is there still more debugging to be done here?
(app starts at ~15MB, ends up at 20.1MB)
Starting at 15 megabytes and levelling off at 20 megabytes is great. This app is tiny, and its memory usage levels off quickly. Problems arise when memory usage keeps rising forever, typically on the level of giga bytes, i.e. many orders of magnitude bigger! So, don't worry, be happy.
Related
I ran the instruments 'Leaks' tool to test if my app has any leaks, and it showed me that I have some leaks. I'm not an expert at fixing leaks, I was wondering if
I have a leak, and
What I should do to fix it.
You would appear to have a leak, but it looks modest. You can click on the little arrows next to the memory address and it should take you a screen in which you can drill in and see where that memory was allocated, which is the first step in figuring out why it wasn't deallocated. (I'd start the non-malloc objects, as more often they map more directly to your code and it's easier to diagnose).
But sometimes you'll see modest leaks like this which are, as Mike Robinson said, false positives. And even if it's not a false positive, it could be coming from the OS, itself, not your code. So we sometimes go through an exercise of really stressing the app (e.g. repeatedly running through the portion of the app that seemed to generate the leak) to see how rapidly the leak grows, if at all. It looks like your your leak might add up to less than 1 kilobyte or so, and doesn't continue to grow, you might choose to not worry about it. (Or at least once you've satisfied yourself that there's nothing in your code that causes it.)
Personally, though, I'm less concerned about these modest leaks than the significant growth in overall memory usage. It might just be an appropriate caching of images, or it might be a sign of some abandoned memory (which leaks tool won't show you). I'd try simulating memory warning and see how much of that memory is recovered. You can also drag across the timeline and go to the allocations view, and you can see what accounts for that memory consumption. You might want to make sure you don't have some deeper memory problem unrelated to the modest leaks reported by "Leaks" tool. Not all memory problems appear in "Leaks": The "Allocations" growth can also indicate problems, and I'd be a little worried that you're not seeing your memory usage drop down to some steady-state level.
Apple shared an example allocation graph, advising us to watch out for the red "wasted" memory. The warmup portion is not so critical, nor is the intermediate level (as long as it's not too high), but the growth of the steady state level is indication of a more serious memory problem:
In your case, I'm not seeing the app return to a steady state at all, which is why I'm a tad concerned. But I'm not sure how much you exercised the app or whether you gave it a chance to return to that steady-state.
If you watch (the somewhat dated, yet still relevant) WWDC 2013 Fixing Memory Issues, it will arm you with tools and techniques for diagnosing and resolving memory issues. It is where the above chart came from and describes it in greater detail. Note, the PDF presentation is nice, but the video is much better, as it includes some practical demonstrations for using Instruments. The WWDC 2012 iOS App Performance: Memory is also good. (It looks like there might be problems streaming the videos, but it looks like you can still download it.)
This question already has answers here:
Problems with memory management, autorelease, permanent heap is sometimes 250+ kb on iOS
(2 answers)
Closed 9 years ago.
Hello my question is maybe General I am not asking for code etc.
I developing only for iPhones with iOS6.1 and above
When I run my application the RAM it uses only grows up(when I switching between views (I have like 15 views)).
However after I ran test with analyzer it didn't find any leaks.
also no leaks were found in instrument leaks.
Despite my application doesn't exceed 20 mb of RAM I am still worried that something may be just not ok there.
I am using ARC ,but the ram still goes up.
Is there any way I can check what can cause 1 sided ram allocation ?
If the memory continues to go up, it could be a variety of different things, but "strong reference cycle" is the prime suspect. Sadly, this won't necessarily show up in Leaks tool in Instruments, either.
Do snapshots/generations in the Allocations tool and identify what's not getting released (notably if it consists of any of your classes) and go from there. Specifically, run the app through its paces, then mark snapshot/generation, do a bit more, and then mark another snapshot/generation. Look at that second snapshot and see what's been allocated (but not released) captured since the prior snapshot, with a focus on your classes. You'll find the culprit pretty quickly that way.
See WWDC video iOS App Performance: Memory for practical demonstration.
For example, here is a healthy app that I profiled through the Instruments' "Leaks" tool, but I'm going to focus on the "Allocations" tool:
In this profile, I waited for the app to quiet down, tapped on "Mark Generation" button (resulting with "Generation A", the first flag in my timeline). I then went to a view and then dismissed it, and did "Mark Generation" again, getting "Generation B". The "Growth" column is telling me that between Generation A and B, 100kb was consumed, but not released. But I'm not worried about this yet because there might be some iOS internal cache of UIKit elements. So, I repeat this process one more time to get "Generation C". Now that's interesting, now reporting a growth of only 8.26kb, which is negligible. This, combined with a clean bill of health from the Leaks instrument makes me feel pretty good about the risk of any serious memory problems.
Now, let's contrast that with some code that has a seriously problematic "strong reference cycle":
Now this is a completely different picture, even though the process was the same "present and dismiss" process, repeated twice. This is now telling me that I had a 14mb growth between generations, and more notably, I can clearly see the problematic growth curve. What's remarkable is that while the Allocations tool is clearly catching a serious problem, the Leaks tool reports nothing.
Now, in practice, the real-world experience with the Allocations tool will probably rest somewhere between these two extremes. Your app may have its own caches or model objects that slowly take up memory, but if you're properly responding to memory warnings, you should be recovering that memory. Frankly, though, most well designed apps should not be generating memory warnings at all (usually accomplished by properly configuring caches, avoiding imageNamed where appropriate, moving to persistent storage for large or infrequently accessed data, etc.). The goal is to get to a point where the app stabilizes around some reasonable baseline memory allocation level, consistently returning back to that baseline.
But it's going to be impossible for us to advise you until you do some basic profiling of your app and diagnose the sorts of memory issues that you're having.
When running my app in the simulator and analyzing its memory allocations using Instruments, the App runs very slow, it runs at less than 1/30 of its normal speed.
The app uses about 50 MB RAM and has approximately 900,000 life objects (according to Instruments).
Could this be the reason for the slow performance?
When running in the app on the device or in the simulator without using Instruments, it performs well (except the memory issue I am trying to debug).
Do you have any idea on how to solve this issue?
Did you encounter slow performance using the Memory Allocation
instruments?
Would you consider having more than 900,000 life
objects "concerning"?
Considering your Analyzer performance issue
In your specific case monitoring the app over a long period of time will not be necessary, as you reach the state of high memory consumption very soon. You could simply stop recording at this point. Then you won't have problems navigating through the different views and statistics to find the cause of the memory issue.
Analyzing the memory issue
Slowing down is normal. 1/30 sounds quite alarming.
You probably should track how the amount of life objects and the memory usage change while you use the app.
It is difficult to decide if a certain amount of life objects at a specific point in time is critical (though 900,000 seems very high).
In general: if life objects and memory usage grow continuously and don't shrink, that is a bad sign.
If you take a look Statistics -> Object Summary (Screenshot), Live Bytes should be a lot smaller than Overall Bytes and the amount of #Living objects should be a lot smaller than the amount of #Transitory objects.
The second thing you can look at, is the Call Tree view.
It gives you a nice overview of which parts of the application are responsible for reserving large amount of memory:
Possible solutions
Once you detect the parts of your code that are responsible for reserving the large memory amount you can look for retain-cycles or you could try to use more autorelease pools in that spot.
Check that you have enough available disk space. I had 8gb left and it seems like that was too little. Instruments was extreeemely slow. Used a minute just to start and didn't quite get around at all.
I cleared out more disk space and then it suddenly went back to being fast as before.
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'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!