It's the first time I have to fix a memory problem from one of my iOS apps, so I'm not really sure how to track it, I've been reading some post from different blogs and I've found that my memory is constantly increasing:
The problem is that I don't know how to track and fix the problems in my code, and I also don't know what should be the best "Growth" in memory. Thank you.
First, I'd recommend watching WWDC 2013 Fixing Memory Problems and WWDC 2012 iOS App Performance: Memory videos. They are dated, yet still relevant, demonstrating of taking the above screen snapshot down to the next level in order to identify the underlying source of the memory issue.
That having been said, I have a couple of observations:
In my experience, nowadays, the problem is rarely "leaked memory" (i.e. memory that has been allocated, for which there are no more references, but you neglected to free it). It's rather hard to leak in Swift (and if you use the static analyzer on Objective-C code, the same it true there, too).
The more common problem is "abandoned memory" resulting from strong reference cycles, repeating timers not getting invalidated, circular references between view controller scenes, etc. These won't be identified by the Leaks tool. Only by digging into the Allocations tool.
First, I often like to see what accounts for the bulk of the abandoned memory. So I double click on a top level call tree and it will show me where the abandoned memory was allocated:
Note, this doesn't tell me why this was abandoned, but by knowing where the bulk of the abandoned memory was allocated, it gives me some clues (in this example, I'm starting to suspect the SecondViewController).
I then drill into the generations results and start by looking for my classes in the allocations (you can also do this by manually selecting a relevant portion of the allocations graph, as discussed here). I then filter the results, searching for my classes here. Sure, this won't always be the most significant allocation, but in my experience this sort of abandoned memory almost always stems from some misuse of my classes:
Again, this is pointing me to that SecondViewController class.
Note, when looking at generations, I generally ignore the first one or two generations because they may have false positives stemming from the app "warming up". I focus on the latter generations (and make sure that I only "mark" a generation when the app has been returned to some-steady, quiescent state.
For the sake of completion, it's worth pointing out that it's sometimes useful running Instruments with the "Record reference counts" feature on the unreleased allocation:
If you do that, you can sometimes drill into the list of retain and release calls and identify who still has the strong reference (focus on unpaired retain/release calls). In this case it's not very useful because there are just too many, but I mention in because sometimes this is useful to diagnose who still has the strong reference:
If you're using Xcode 8, it has a brilliant object graph debugger that cuts through much of this, graphically representing the strong references to the object in question, for example:
From this, the problem jumps out at me, that I not only have the navigation controller maintaining a reference to this view controller, but a timer. This happens to be a repeating timer for which I neglected to invalidate. In this case, that's why this object is not getting deallocated.
In the absence of Xcode 8's object graph debugger, you're left to pouring through the retain and release references (and you can look at where each of those strong references were established and determine if they were properly released) or just looking through my code for things that might be retaining that particular view controller (strong reference cycles, repeating timers, and circular view controller references are the most common problems I see).
Minimizing your app's Memory Footprint
For the benefit of both stability and performance, it's important to understand and heed the differing amounts of memory that are available to your app across the various devices your app supports. Minimizing the memory usage of your app is the best way to ensure it runs at full speed and avoids crashing due to memory depletion in customer instantiations. Furthermore, using the Allocations Instrument to assess large memory allocations within your app can sometimes be a quick exercise that can yield surprising performance gains.
Apple Official Document
Related
I have deinit method in all my VCs to check if they are being deinitialized or not
deinit {
print("\(self.description) successfully deinitialized")
}
When I logout, I see all my view controllers are deinitialized and my LoginViewController becomes the root view controller. But I can still see the RAM usage as 90MB. Every time I log in and logout, it keeps increasing.
Is it possible if my view controller has deinitialized, still it can have a memory leak?
What I know is, if the view controller is deinitialized, there is no memory leak. If there is a memory leak, the view controller will not deinitialize.
Am I wrong? Please help.
UPDATE: As per Luca's suggestion, and with the help of this answer, I found out the memory leaks. But I am not able to understand any of this. Is there any way to make it more readable?
There are three types of undesired memory issues:
Abandoned memory: This is memory that you do have some lingering strong references, but for which you no longer have access to these references. The typical example is a strong reference cycle.
This is very common problem in Swift projects. You find these using the “Debug Memory Graph” feature. See iOS app with ARC, find who is owner of an object or How to debug memory leaks when Leaks instrument does not show them?.
By the way, you’ve apparently confirmed that your view controllers are being released. That’s great if that’s the case. But they’re not the only object that can be entangled in a strong reference cycle. Any reference types can get caught up in reference cycles. Use the “Debug Memory Graph” feature and review your objects in the left panel and make sure there’s nothing there that shouldn't be. (By the way, this technique saves you from having to sprinkle deinit methods all over the place.)
Cached memory: This is memory that has been allocated, and the system will hang on to it in case you might need it again, offering you high performance access. This often isn’t too worrisome unless the amount of memory consumed is huge, because when the device is placed in memory pressure, often this memory is reclaimed automatically for you.
If you use UIImage(named:) that can cache images in memory. If you use URLSession, the URLCache can cache responses.
If testing on simulator, you might want to choose “Debug” » “Simulate Memory Warning” and see how much memory is retrieved, if any.
Leaked memory: This is memory that that has been allocated, but for which you have no further references to it. Typically this is a result of some manual allocation of memory that was never deallocated.
The Leaks tool will help you find these, but this is relatively uncommon to have anything material in this category in Swift code. (Or if you do have them, they tend to be small things generated within the frameworks, not within your control.) Sure, if you’re doing manual Core Foundation (or Core Graphics) calls, you can manifest these sorts of issues, but it’s not very common.
FWIW, the leaks tool doesn’t appear to be reporting any material memory consumption. So focus on Allocations tool (or, better, the “Debug Memory Graph”, if you can). Your leaks don’t appear to be enough to account for the sort of memory loss that you’re talking about here.
Also, I’d suggest you confirm that you do not have memory diagnostics turned on. E.g. things like zombies or the like can cause growth in memory (though I wouldn’t have thought that you could approach 90mb worth). Tools like this, the malloc stack trace, etc., increase memory consumption. That’s fine while using these tools to diagnose issues, but should be turned off when you’re done.
But, bottom line, I’d focus on confirming that the problem is not abandoned or cached memory, before diving into leaks (as often the latter are small and unrelated to anything in your code). These are far more common source of issues.
Even though the ViewController is de-initialised it doesn't necessarily mean it will be deallocated. There is a possibility there is a strong reference cycle. Or maybe in your code, to avoid retain cycle you are using unowned reference somewhere, for which the reference count is not becoming zero. The object will not be de-allocated until both strong and unowned reference counts become zero. In that case try using weak instead of unowned and see if it works. Weak references point to something called side table instead of directly pointing to the object, and hence don't keep the object from getting deallocated once de-initialised
Check out this article to understand how unowned reference can sometimes keep the object from getting deallocated, and ARC in general.
My application has memory issues where after a certain threshold, the application will crash.
I've been instructed to use Instruments and select the Allocation option.
However, I can never seem to get a "direct" answer to my issue, I have attached screenshots below to help describe the issue better.
The memory issues are not linked to any ViewControllers or files that I have created. Rather other libraries/frameworks which I had no idea were being used. I have been battling this issue for a few weeks, I have altered my code/implemented a variety of methods which I believed may have resolved the issue. However, no luck.
Could someone please tell me how I can combat this issue? As I cannot seem to force the memory to be released which in turn means I have this giant memory bug within my application.
Thank you.
Edit - Added a screenshot of the memory usage when viewing an image at full resolution and returning to the home screen.
A couple of thoughts:
See this answer which talks about using “Debug Memory Graph” tool featured in WWDC 2016 video Visual Debugging with Xcode. That tool is often easier to find issues than Instruments. It organizes the reference counting types by target/framework, making it much easier to sift through the results.
But if you’re dealing with non-reference counted malloced data, then Instruments is the way to go, with all the complexity that entails. But “Debug Memory Graph” is often a better first line of defense.
You said:
The memory issues are not linked to any ViewControllers or files that I have created.
Make absolutely sure that your classes aren’t any buried down there lower in the list. There will be far fewer and the sizes are smaller, so they won’t appear up at the top and they’ll be buried in the list even tho they’re likely to be the root of the problem. Frankly, if your app is running, some of your classes have to be in there somewhere. Lol.
Again, the “Debug Memory Graph” approach helps identify your own objects much more easily than Instruments.
If possible, I’d suggest running the app, returning back to some home screen where you expect stuff to have been released, and repeat that process a few times. The first time you return to a quiescent state is not very illuminating, because there’s going to be a lot of internal caching going on. But the subsequent times you exercise the app and return to that home screen, you’ll have a better example of what’s getting allocated and not released without all of this noise of stuff the OS did on the first iteration:
(Taken from WWDC 2013 Fixing Memory Issues.)
Hopefully, the “warmup” memory isn’t too dramatic, but the red area is what we often focus on, as this is what is “wasted” as we continue to use the app (resulting in eventual crashes).
Unfortunately, your allocations curve isn’t showing it drop at all, which is worrying. Now, maybe you don’t have a “home screen” to which you can return, so maybe this isn’t relevant. But even in that scenario, you should have some state in your app that you can see memory being recovered. It’s hard to say on the basis of the information provided.
You haven’t mentioned it, but confirm what debugging options you have. For example, if you have zombies turned on, you might not see memory drop back as much as it should. Often when we first encounter these sorts of issues, we start flipping on all of these debugging options, but they have an impact on the memory profile of the app. So if you’ve been turning on things like zombies or what have you, you might want to make sure you turn them back off to make sure they’re not part of the behavior you’re seeing.
I’d suggest simulating memory warnings and see if you can see memory being recovered. Make sure your code is observing and responding to memory warnings, purging memory where it can.
This is all general advice and we can’t offer specific counsel without seeing what your code is doing. I’d suggest you create a copy of your project, prune out unrelated stuff, and keep doing that until you have the smallest possible reproducible example of this unbridled memory growth. Often that process will be enough for you to diagnose the problem on your own. But we can’t pour through tons of code. We need a minimal, complete, and verifiable example of the issue.
Bottom line, “Debug Memory Graph” is often our first level of analysis. Run the app, identify what objects you expected to be released but weren’t, and go from there. Also keep an eye on how many of these objects are out there (e.g. if you see the same view controller multiple times, that’s a sign of a strong reference cycle or some circular invocation of view controllers).
Instrument just shows you overview of how your app handling memory/CPU etc. When you find something in instrument, you have to make changes in code.
Refer this: For that you should understand how stong and weak works in iOS.
Once you understand ARC in iOS. You will understand your memory leaks in your code.
Trick is :
Try to check number of objects are not getting removed from memory in instrument.
Then check code for strong reference of object and try to remove unnecessary strong references.
Hope this will help you.
I fixed the issues which I had. Just in case anyone in future experiences the same issues this is what I did:
If declared my outlets/delegates as weak
Wherever I used the keyword self in a block where XCode demanded I use self, I added either [weak self] in or [unowned self] in
Remove any instances of the word self which were not required
Added a deinit and included a print statement
Added a breakpoint where the deinit method is called
Commented out functions in my viewDidLoad method and go through each one to see which one/if one caused the issue.
I am working on one application which has lots of screens, custom views and API calls on almost each screen. Sometimes when the user uses the app for a long time, it gets crashed because of Out of Memory issues.
Ideally, I followed all guidelines while developing app and ARC takes care of memory usage. So what are the best way to avoid such memory issues?
Code has a built-in memory profiler that can help you with this issue - for a tutorial on how to use it, this might be helpful http://www.raywenderlich.com/23037/how-to-use-instruments-in-xcode
Otherwise, if dealloc isn't being called it could be a symptom of a retain cycle (two objects maintain strong references to each other, so they are never deallocated).
As I learnt from the apple documentation that ,In iOS ARC will automatically take care of the memory leaks and memory management.
But my doubt was, do we still need the role of Xcode instruments (Allocations and Leak) to ensure whether memory leak has happened in our application??
Please do share if you know the solution.
Yes, of course you need to use Intruments.
Swift uses Automatic Reference Counting (ARC) to track and manage your app’s memory usage. In most cases, this means that memory management “just works” in Swift, and you do not need to think about memory management yourself. ARC automatically frees up the memory used by class instances when those instances are no longer needed.
However, in a few cases ARC requires more information about the relationships between parts of your code in order to manage memory for you. This chapter describes those situations and shows how you enable ARC to manage all of your app’s memory.
You should take a look over Automated Reference Counting.
One of the most common situation is when you have strong reference cycles between class instances, because the compiler doesn't know when to release that part of memory. Also take a look over the differences of strong and weak references.
But as even Apple saids, "In most cases", you should be ok without, but if your application crashes, it could be that you have memory issues.
Automated reference counting provides a new, simpler, way of managing reference counted objects. By automating the tasks of calling retain and release it eliminates a large class of memory leaks and invalid references caused by programmers forgetting to call memory management functions.
However, ARC does not eliminate a different class of leaks caused by logical errors in the design of your code, when your object graph has cycles. ARC provides tools for you to address this issue by adding weak references, but if you don't do it right, there is nothing ARC can do to help you.
In addition, you may have "lingering references", when an object remains in memory even though your program no longer needs it. Memory leaks of this kind can happen even in garbage-collected environments, such as Java and C#. They represent a logical error in design, and cannot be eliminated by clever compiler tricks in the current state of compiler technology.
This is when Xcode memory tools come in handy. You run them to check for memory leaks, ensuring that your code does not have cycles and "lingering references".
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.