I’m facing memory issues in my app and I didn’t find a way to find out yet which objects/classes are using that memory.
The app is simple, a view controller with a gallery view of images (grid view just like Instagram Explore; collection view with xib cells) and when you tap one, it takes you to the next screen, which is the same set of images, but as a vertical list (uitableview with xib cells). The images are downloaded asynchronously from web.
The memory used by the app is continuously increasing when I scroll in both screens and also faster every time I open the list screen. Then the only moment when the memory used is reduced (and I mean drastically, like from 1.8GB to 200MB) is when it hits the device’s limit and then the issue appears again and again. Also, sometimes, the system fails to reduce the memory used and the app crashes ("Terminated iOS app due to memory issue").
I don’t think it’s a layout problem, I’ve checked all of that, also used memory graph debugger and only “malloc” issues found there which takes me nowhere, no class, no line, no nothing. Also, Instruments tool is too complex and I don’t know how to deal with it yet.
I’ve read some tutorials and tried some solutions but nothing worked. There are included: https://krakendev.io/blog/weak-and-unowned-references-in-swift, http://iosbrain.com/blog/2018/07/22/finding-memory-leaks-with-the-xcode-memory-graph-debugger-and-fixing-leaks-with-unowned/, https://www.youtube.com/watch?v=1LnipXiSrSM&t=1697s, https://developer.apple.com/videos/play/wwdc2018/416/
Can somebody give me some other advice or tutorials on how to properly debug memory issues to be able to find out their exact origin?
Most contemporary memory debugging strategies are geared for identifying and resolving strong reference cycles. But that’s not your issue here. The fact that most of the memory is recovered when you face memory pressure points to caching issues. Whatever further diagnostics you do is only likely to confirm this behavior.
To address this, set reasonable limits to your caches and avoid caches that don’t give you that control (e.g. UIImage(named:)) and the problem will likely be resolved. We cannot comment further without seeing how images are being retrieved (e.g. make sure the cache for the URLSession is reasonable) and how they’re being cached once downloaded (e.g. third party async image retrieval libraries generally give you control over the cache).
And, assuming you (or your third-party libraries) are caching, make sure that:
Test your app on the simulator, manually choosing “Debug” » “Simulate Memory Warning”. This will help confirm whether the app is responding to memory pressure. On the basis of what you describe, I think we already know this is the case, but it’s a good diagnostic.
Note, while we always want to make sure our apps respond to memory warnings correctly, when you face memory warnings, it may already be too late (e.g. the app may be doing a series of allocations and may fail before your app has a chance to react to the warning). You want to do whatever you can to manage caches before memory warnings occur.
You’re caching the original payloads (the Data object that contains the compressed jpg/png asset) and not UIImage objects (which, once they’re used, are uncompressed and can be huge if you’re not careful), or
If you do cache the UIImage objects, make sure to resize them as appropriate for your UI.
For example 100x100 image at 3x scale will take up 120kb, but if the image is 1000x1000px, even if the image view is only 100x100pt, the uncompressed image will take 4mb, i.e. 4 bytes per pixel, regardless of size of compressed jpg/png payload.
If you are using NSCache, set countLimit or totalCostLimit.
If you are doing your own collection (array or dictionary) of downloaded images, make sure you respond to memory pressure. E.g., in Swift:
NotificationCenter.default.addObserver(forName: UIApplication.didReceiveMemoryWarningNotification, object: nil, queue: .main) { [weak self] _ in
// do whatever you need to remove your cached objects here
}
I don’t think that’s the issue here (because your app is responding to memory pressure), but you might want to review your app for anything else that (a) is large; (b) you are downloading and holding in memory yourself, and respond accordingly.
FWIW, I think you’ve done sufficient diagnostics to identify the source of the problem (the fact that it’s getting purged under memory pressure really does point to caching issues), but if you want to learn the “Allocations” tool in Instruments, check out these old WWDC videos Fixing memory issues and iOS App Performance: Memory. They’re old and focus on Objective-C, but the techniques outlined there are still applicable if you want to get up to speed on Instruments. But, like I said, I think you’ve already identified the issue.
Related
I have an app that has two view controllers with images
when I launch the app the memory increases and when I then segue to the other view controller and load the 2nd image ( I have a button to do this ) the memory goes up again.. this is what I would have expected..
However, when I dismiss the 2nd VC controller or removing the image in the 2nd VC via a button, and then dismiss the controller , the memory never goes down.
why is this, with an app with lots of images it could get large.. how do I release the memory of the 2nd VC or at least the memory of the 2nd image.
Im looking at memory under debug navigator in Xcode 8, when running the app.
xcode project can be found here , very simple
iOS will cache images. The documentation for UIImage(named:) says:
Discussion
This method looks in the system caches for an image object with the specified name and returns the variant of that image that is best suited for the main screen. If a matching image object is not already in the cache, this method locates and loads the image data from disk or from an available asset catalog, and then returns the resulting object.
The system may purge cached image data at any time to free up memory. Purging occurs only for images that are in the cache but are not currently being used...
Special Considerations
If you have an image file that will only be displayed once and wish to ensure that it does not get added to the system’s cache, you should instead create your image using
imageWithContentsOfFile:. This will keep your single-use image out of the system image cache, potentially improving the memory use characteristics of your app.
Note, you don't control when this cache is freed. The OS does that as it sees fit (and generally in response to memory pressure).
Bottom line, we often don't worry about inherent caching by UIImage and simply ensure the app doesn't have its own memory issues, e.g. make sure deinit is getting called and/or use the "Debug Memory Graph" feature in Xcode to watch memory graph. Or if you want, programmatically set the image using UIImage(contentsOfFile:).
I'm having a memory issue with one specific ViewController and memory. When I launch my app in debug mode, it launches with 40mb memory usage. (I have no idea if this is already a lot or not -- what is common?). Then, when I open this specific view, it spikes up to about 120mb. The issue is, when I pop this view (with the navigationController popViewController), the memory stays up at 120mb. And then, when I re-open it, it spikes to 200mb (a 80mb increase every time).
With other similar ViewController it spikes up to 120mb too, but when I close the view, it goes back down to more or less 40mb.
The problem is that this specific view contains quite a bit of code (about 1000 lines...) and it's impossible for me to post everything here.
What methods should I use to specifically locate the issue in xCode?
For anyone I might be able to help with this:
use the tools in xCode as recommended. There are some great tutorials online.
In my case it was an issue with an [NSTimer] which kept a strong reference to my view, so it never got released afterwards, thus stacking up memory. Make sure to stop your time when you pop a view.
I am new to IOS and coming from pcs I am trying to get a feel for managing memory and battery resources.
I have a UIImageView subclass which is is very light (it's an image placeholder with a handful of methods). This subClass is repeatedly added to, and removed from, several root views. I hold an IBOutlet reference to the subClass on each root view.
Would you:
Simply add/remove the subClass from the root view (and keep it in memory)?
Or
Create and destroy the subClass instance each time (freeing the memory but extra work)?
Is this worth considering or am I worrying over nothing?
Would your answer be the same if the UIImageView subClass was very large?
Is there a way of measuring these matters?
In general, you can use UIImageView with disregard on iOS. UIImageView is not much more than a clipping region with some methods for dealing with images.
The resource which takes up more memory is the UIImage contained by the UIImageView and these are cached by iOS.
So there is little cost to creating UIImageViews and destroying them, just take care that you dealloc them and don't leak memory and you should be fine unless you have hundreds of them.
The subclass being "large" doesnt matter, the code in a class is never loaded more than once. If the class has alot of extra state - that will take up some memory but I cant image a whole lot of that in a UIImageView.
You better just keep it in memory. User experience will be much better if you don't slow down the device by continuously recreating the image view. One single poor UIImageView instance is not memory heavy enough to worth the time and effort to recreaty all the time.
Keeping it an memory to improve performance isn't as much of a hit as you would think - taken from the other answers you have. But you should remove it from memory if you get a memory warning, and provide a way to bring it back to life as needed.
I would go with your first option, keeping it in memory. This is just because I think it is handy to keep my views alive and reuse them. This means that they will remember their state and content and that helps out in many cases. I usually write methods to reset the content or refresh it when needed.
Keeping it in memory might also increase the loading of the view slightly, but usually you do not notice the difference, and I see people using both ways.
And yes, I would say you are worrying a bit too much, especially if you think this decision would affect battery.
Regarding memory management, keeping the imageview in memory or releasing/recreating consumes the exact same amount of memory as long as you only have one imageview that you are using. The only difference is that you are allocating and releasing it repeatedly if you are recreating it.
You could however get memory issues if you start creating new imageviews every time the user opens them and never letting go of them, but that means you are doing something fundamentally wrong in your programming.
As long as you know what you are doing, no need to worry about the amount of memory in a case like this - there's plenty. Start worrying if you are handling large images.
Especially if you use ARC there is not much to worry about, just start hacking away. If you are managing memory manually, I do recommend trying to reuse stuff because that causes less headache and risk of giant leaking. Use leaktools and analyze tool to make sure in this case.
I just started learning the instruments tool, and I'm pretty sure what I am seeing is not good. When I look at allocations, specifically the column "Live Bytes" and "Overall Bytes", I see the number continually increases as the app runs...
My app has two view controllers. A table view, and the second view controller displays detailed information about the row they selected in the table view, downloaded from the internet.
I kept clicking a row in the table view, followed by clicking the back button in the navigation bar... and LiveBytes continued to increase.
I'm guessing this means that my objects aren't being released from memory... but please correct me if I'm wrong.
MY QUESTION IS: How do I use the data in instruments/allocations to track down this memory issue? How do I find the objects not being released from memory?
I'm looking for tips on how to use these tools to clean up any memory problems my app has.
Thanks!
XCODE 4.2.1, deploying to iOS 5.0+
EDIT: I'm looking at the #living column and seeing objects like UIScrollView continuously increase... and never decrease. When I click the back button in a navigation bar, are objects automatically released from memory? When are objects released, or do I need to do it manually? Or could I be running into an issue due to using strong pointers, causing objects to not be released?
Whenever you want to observe memory usage in a cyclic pattern, there's the wonderful Heapshot analysis in the "Allocations" instrument.
Start your app and go to a default state.
In Instruments, press the "Mark Heap" button to create the "Baseline".
Do something in your app like pushing a view controller.
Return to the default state.
Press the "Mark Heap" button again to create a heapshot.
Repeat about five times from step 3.
This will result in a list of heapshots, each showing the objects that are still alive from that cycle. If your app has no leaks there will be no objects left in the middle heapshots.
The first one or two cycles might have warmed up some caches, the last two might not have cleaned up some reused resources. That's why it's usually a good idea to make four to six heapshots.
The magic in the heapshot analysis lies in the fact that the heapshots show you the leaked objects from previous cycles and remove them automatically when the objects are released later. In contrary to the "Leaks" instrument it also finds abandoned memory, not only leaks.
Most Probably you have discarded the arm64 and are running your app with armv7 only. Add both arm64 and armv7 as architectures
I think one of the best ways to solve memory issues is to use ARC.
Edit -> Refactor -> Upgrade to Objective-C ARC.
ARC will handle the majority of memory management in your app. Especially sice your app doesn't sound too complex, it might totally eliminate your problem. You still need to watch out for retain cycles and listen to memory warnings though. If you don't want to use ARC (which you should) at least run the static analyzer. Your problem might be something simple that the static analyzer can show you how to fix.
Edit:
You mentioned scroll views- this might be your problem: Memory leak every time UIScrollView is released
The profile tool has an instrument called 'Leaks'. It is similar to the 'Allocations' instrument but it shows you the object that was not released. May be you can use the 'Leaks' tool to find what is the object that was retained and release these object.
So I installed my app on my ipad and it crashes due to a memory issue. I figured this was because I'm using really big image files so I went back and reduced them all and essentially cut out about 75% of their size by resizing them and then using PNGCrush.
Now, as for running the program, if I have a background image for each of my 4 individual tabs, would it save memory if I were to set the images to Null every time I switch tabs or should I leave them set? I have one page which has a couple dozen images on it as they act as buttons and from what I'm hearing it sounds like I should clear them all when I'm not viewing that page. Is that correct?
Right now when I boot up, I load all of the images for everything in the app in a sort of: "load it now and be done with it" mentality, though from what I've been reading that causes memory issues as there isn't much memory to use. Does this mean every time I switch tabs or views I want to clear all the images from the ones that aren't visible and then reload them when we go back to them? Would this cause an increase in performance? Or at least prevent crashes? My program works in the simulator but when I run it on my ipad it just explodes =/
Thanks!
EDIT: I'm using Monotouch BTW hence everything is in C#
UIImage BG = UIImage.FromFile("Images/Makes/explosion.png");
UIImage basic = UIImage.FromFile("Images/Models/camaro.png");
UIImage advance = UIImage.FromFile("Images/search.png");
AdvancedSearchButton.SetBackgroundImage(advance, UIControlState.Normal);
ImageSearchButton.SetBackgroundImage(basic, UIControlState.Normal);
MainBG.Image = BG;
BG.Dispose();
basic.Dispose();
advance.Dispose();
Now I know in regular C# dispose() doesn't actually "free" memory, but I read something that says that it gets converted to a "release" when it compiles over to obj-c so that would essentially be freeing those objects.
Also, I'm wondering if I would need to dispose() of the individual buttons and the image after I'm no longer viewing them. I was just setting the image to NULL but that gave me errors.
MainBG.Dispose();
AdvancedSearchButton.Dispose();
ImageSearchButton.Dispose();
Thanks so much for the help!
EDIT2:
So I just tried the above code and the background images and everything else are still there and appears as if nothing is actually getting cleared. Suggestions?
The iPhone does not have virtual memory and it does not have garbage collection. So once something is loaded in the memory it stays in the memory until your code explicitly releases it. If you are not using some resources, you should definitely clean them up as soon as possible.
Also, you should listen to low memory warnings from iOS, which is another opportunity for your code to do some internal clean up.
First off, lazy load your resources unless you have a justifiable reason not to. Secondly, I'm not sure how big your images are, but on large images (ones that are by nature intended for things like backgrounds, or otherwise), generally speaking, I will chop them up into chunks, and load them, again you guessed it, lazily as they're needed.
What you should do is handle your memory warnings properly. Deallocate any resources that are not absolutely critical -- i.e., items on other views in other tabs, or hidden content. You can load them again when you have to.
You should also take a look at using Instruments to find out if you leak (I'm hoping you've already done this), examine your program to see if you can persist some cached resources to disk during low memory situations, etc.