I’m currently working on a tvOS app. This is my first native (Swift) app. The app will be a digital signage app, used during events or in offices of companies.
One big difference compared to a typical app on iOS/tvOS is that it needs to run pretty much 24/7, so memory is a big topic for this app. The smallest leak will eventually cause the app to crash.
The app is constantly looping through a set of fullscreen slides. At the bottom of the screen there is a ticker with 10 articles (refreshed every 10 seconds - now during development). Below is a screenshot of the weather slide, to get an idea.

Currently the app is crashing after a period of time and I’m pretty sure I’ve narrowed it down to the ticker component (when disabling it, the app lives for days). If I use the ‘Leaks’ preset in Instruments I get the following result:

It looks like it’s leaking Article instances. I’m recreating Article instances every 10 seconds and providing them to the ticker component. I think that is why new instances leak every ~10 seconds.
Before I started using the ‘Leaks’ preset in Instruments, I used the ‘Allocations’ preset, while using this all seemed fine to me. But I’m probably misreading the results…
Using allocations:

The way I read this is that currently 10 Article instances exist in memory, and 31 have existed but are cleaned up now - so I’m safe.
But the app still crashes.
I’ve read a lot on retain cycles, implemented weak/unowned where I believe I should.
So my question is not so much about code, but more about how to read this data, what does a Leak mean in this context, and why do I see these ‘leaks’ not as persistent objects in the Allocations window?
(tests are done on multiple devices + simulator)
If you see a steady (i.e., approximately n GB / minute or hour) increase in memory usage in Instruments, that is a good sign that objects are being created, but not dealloced. Your allusion to weak and unowned vars makes me think that you know this, but you may not have found all sources of your leak. I would suggest taking a few generation summaries in Instruments, and looking at specific classes/objects in Heap allocations. Your problem classes will steadily increase in number, and likely never decrease. Try to debug the problem from there.
As for what 'leak' means in this context, it's what it always means: Your computer is not releasing memory resources. It may seem different, because we are used to thinking of a leak as something that eats through memory at a much faster rate (like an infinite loop running on four cores, or something), but that kind of leak and this are actually the same thing; yours is just slower.
I’m back after weeks trying to figure out what was wrong. The good news, I found my leak, and solved it!
The issue was solved by removing a closure inside another closure keeping a reference to a variable in the first closure. This caused a retain cycle.
I really don’t understand why I didn’t find it earlier, I asked a new question for this here: getting-different-data-in-instruments-based-on-method-of-profiling.
Related
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.
I’m having a memory issue. I get an error message “Thread 1: EXC_BAD_ACCESS(code=1,address=…) The error is elusive, and apparently timing-dependent. On my iPad mini, it usually averages about 3 minutes between crashes, on my iPhone SE it averages 10 minutes, and on the simulator it has run for 20 hours without crashing.
I’m not successful in using the Zombie detector. (See below for my experiences with it.) So I’m trying to go over my code with a fine tooth comb to see what might be causing the crash. What are the issues that might cause an allocation error?
My app is multi-threaded. It takes sound from the microphone, processes it, and continuously updates the display with the processed results. It’s written in Swift 3, so I’m not doing any explicit mallocs or frees.
I’m looking for places in the code that might cause an error. The two things I’m looking closest at are weak references and unsafe pointers. Are there any other common programming errors that could trip me up?
(The Zombie detector is useless. The Apple Instruments User Guide says, “For iOS apps, use the Zombies template with the iOS Simulator, rather than a physical device.” I’ve ignored the warning and tried it with my iPad mini, and I can’t get it to crash. Everything runs at about 1/10 speed, and when I pause the recording, my OSX machine gets sluggish as well, displaying the spinning “Wait” cursor for minutes at a time. The total memory allocation goes up and down, but stays within limits, so there is no major memory leak. I’ve also run the Zombies instrument on the simulator as well. It’s equally sluggish, and it still doesn’t crash.)
The issue in this case turns out to be thread safety. If I set a UInt8 variable on one thread and read it on another thread, it can lead to a memory error. The setters and the getters are not thread safe.
(I’ve been programming computers most of my professional life, mostly in C, C++, and Java. I’m familiar with the multi-threading issues in these languages. I’m new to Swift 3. It simply hadn’t occurred to me up to now that setting and getting numeric variables was not an atomic operation — that an ill-timed access could cause a program to crash. I’m going to have to drastically rethink my approach to concurrency with Swift.)
Recently, I’ve received a quite large and ugly legacy code written in Swift 1.2 with full of singletones and managers referencing to each other. One of my task is to clean this up and get to the point of initial home screen - when all managers, views, singletones are stopped and nil’ed.
My current approach for tracking leaks was simple. In every meaningful classes I was counting instancesCount, a static variable which was incremented in init and reduced on deinitialization process. Of course, in XXI century with those all mature tools, its not smart idea for finding allocation leaks but... I don’t know why, Xcode instruments wasn’t very helpful. They were indicating troubles where everything was fine according to my approach. For example, instruments were saying that one of my manager is never deinitialized - which was not true, as instanceCount was 0 for this particular manager in the initial home screen. Weird.
Anyway, all this could close up with question: how to detect why my instance couldn’t be deinitialized and who keeps reference to it?
Reading code line by line and finding places of retain cycles is pointless as codebase is very complex and time - like always - is limited.
Use Instruments with Allocations Trace Template. It tracks reference count changes for each object and shows when it was increased and when it was decreased. No need for manual monitoring here.
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.
I have a strange memory issue I'm having problems resolving and would appreciate some advice as to where else to look.
The program I have (iPhone App) has a function whereby it basically downloads loads of files, processes those that are JSON, and stores the rest to disk. The JSON processing is CPU intensive and can take several seconds per file, so I have a NSOperationQueue with maxConcurrency limited to 1 that handles all the heavy lifting, and a queue that manages the multiple files to download.
Ever since iOS5 came out, the App has had problems completing the download sequence without crashing and so far what I have tried is;
1) Changed the performSelectorOnBackgroundThread JSON processing to use a single NSOperationQueue so as to limit the number of background threads working with large objects.
2) Added NSAutoReleasePools inside loops that create multiple, large, transient objects.
3) Flushed the sharedURLCache to ensure the files aren't hanging around in the system cache.
4) Stored the JSON objects to disk using NSKeyedArchiver and passed the filenames between threads rather than the actual objects, to again try to mitigate the number and size of retained objects currently in use.
All of these at first seemed to make a difference, and when I look at the memory allocations, I've now got the peak usage down from just over 20MB (hence no wonder it was crashing) to under 10MB, and yet the app is still crashing with low memory as before.
I'm trying to trace what is eating the memory causing the app to crash and on this occasion I'm having real problems persuading Instruments to tell me anything useful.
Here's a typical trace (on an iPhone 3GS running iOS 4.3.5)
You can see that the PEAK usage was a tad over 7MB and yet shortly after, you can see the 2 flags pertaining to low memory, and then low memory urgent, followed by the app terminating shortly thereafter.
If I use the memory monitor, the cause of the crash seems clear enough - physical memory is being exhausted - look at the light green trace below. The low memory warnings co-incide (not surprisingly) with the physical memory running out.
There are no leaks showing FWIW either (I've done that in other runs).
It's not image caches or NSURLConnection caches and the only thing I can think of is that perhaps there are some bad leaks that aren't being detected ... but I'm having issues identifying them because if I click into all allocations to see the objects that are live, and then do a command-A to select them all (in order to paste them into a spreadsheet to see where the memory seems to be), at the point I hit command-C to copy them, Instruments beachballs and never recovers.
I really cant figure out what's going on. Does anyone have some advice on how to persuade instruments to show me some more useful information about what is using this memory?
Sorry I can't post any meaningful code fragments ... hopefully the instruments screenshots at least give you an idea about where I'm coming from.
The Leaks instrument isn't terribly useful for figuring out anything but the obvious leaks in your app.
What you are describing is an ideal candidate for heapshot analysis.
tl;dr Heapshot analysis allows you to see exactly how the heap of your application grows between any two points of time (where you determine the points).