Reputation: 458
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?
Upvotes: 4
Views: 1037
Reputation: 437872
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.
Upvotes: 4