How to debug memory leaks when Leaks instrument does not show them? - ios

I have an iOS app written in Swift that is leaking memory - in certain situation some objects should be released but they are not. I have learnt about the issue by simply adding deinit debug messages like this:
deinit {
println("DEINIT: KeysProvider released")
}
So, the deinit message should be present in console after such events that should cause the object to release. However, for some of the objects that should be released, the message is missing. Still, Leaks Developer Tool does not show any leaks. How do I solve such situation?

In Xcode 8, you can click on the "Debug Memory Graph" button, in the debug toolbar (shown at the bottom of the screen):
See Apple’s Diagnosing and Resolving Bugs in Your Running App: Visualize and Diagnose Increasing Memory Usage.
Just identify the object in the left panel that you think should have been deallocated, and it will show you the object graph (shown in the main canvas, above). This is very useful in quickly identifying where the strong references were established on the object in question. From here, you can start your research, diagnosing why those strong references were not resolved (e.g. if the object in question has a strong reference from something else that should have been deallocated, look at that object's graph, too, and you may find the issue (e.g. strong reference cycles, repeating timers, etc.).
Notice, that in the right panel, I'm seeing the call tree. I got that by turning on the "malloc stack" logging option in the scheme settings:
Anyway, having done that, one can then click on the arrow next to the relevant method call shown in the stack trace in the right panel of the first screen snapshot above, and you can see where that strong reference was originally established:
The traditional Instruments technique (especially useful if using older versions of Xcode) is described below, in my original answer.
I would suggest using Instruments' "Allocations" tool with the "Record Reference Counts" feature:
You can then run the app in Instruments and then search for your class that you know is leaking and drill in by clicking on the arrow:
You can then drill into the details and look at the stack trace using the "Extended Details" panel on the right:
In that "Extended Details" panel, focus on your code in black rather than the system calls in gray. Anyway, from the "Extended Details" panel, you can then drill into your source code, right in Instruments::
For more information and demonstrations in using Instruments to track down memory problems, please refer to:
WWDC 2021 video Detect and diagnose memory issues
WWDC 2019 video Getting Started with Instruments
WWDC 2018 video iOS Memory Deep Dive
WWDC 2013 video Fixing Memory Issues
WWDC 2012 video iOS App Performance: Memory

Use instruments to check for leaks and memory loss due to retained but not leaked memory. The latter is unused memory that is still pointed to. Use Mark Generation (Heapshot) in the Allocations instrument on Instruments.
For HowTo use Heapshot to find memory creap, see: bbum blog
Basically the method is to run Instruments allocate tool, take a heapshot, run an iteration of your code and take another heapshot repeating 3 or 4 times. This will indicate memory that is allocated and not released during the iterations.
To figure out the results disclose to see the individual allocations.
If you need to see where retains, releases and autoreleases occur for an object use instruments:
Run in instruments, in Allocations set "Record reference counts" on (For Xcode 5 and lower you have to stop recording to set the option). Cause the app to run, stop recording, drill down and you will be able to see where all retains, releases and autoreleases occurred.

Related

How to remove Core Data Object from memory? [duplicate]

Is there a tool or method to locate strong references cycles in my SWIFT code?
A strong reference cycle is when two instances of classes reference each other without the proper safeties (weak/unowned) hence preventing the garbage collector from disposing of them once all the variables I created stopped referencing those objects.
The method for finding strong reference cycles is the same in Swift as in Objective-C.
You'd run the app from Xcode, exercise the app sufficiently to manifest the cycle, and then tap on the "debug memory graph" button (). You can then select an unreleased object in the panel on the left, and it will show you the memory graph, often which can clearly illustrate the strong reference cycles:
Sometimes the memory cycles are not as obvious as that, but you can at least see what object is keeping a strong reference to the object in question. If necessary, you can then track backward and identify what's keeping a strong reference to that, and so on.
Sometimes knowing what sort of object is keeping the strong reference is insufficient, and you really want to know where in your code that strong reference was established. The "malloc stack" option, as shown in https://stackoverflow.com/a/30993476/1271826, can be used to identify what the call stack was when this strong reference was established (often letting you identify the precise line of code where these strong references were established). For more information, see WWDC 2016 video Visual Debugging with Xcode.
You can also use Instruments to identify leaked object. Just run the app through Instruments with the Allocations tool, repeatedly (not just once or twice) returning the app back to some steady-state condition, and if memory continues to go up, then you likely have a strong reference cycle. You can use the Allocations tool to identify what type of objects are not being released, use "record reference count" feature to determine precisely where these strong references were established, etc.
See WWDC 2013 video Fixing Memory Issues and WWDC 2012 video iOS App Performance: Memory for introductions to identifying and resolving memory issues. The basic techniques proposed there are still applicable today (though the UI of Instruments tools has changed a bit ... if you want an introduction to the slightly changed UI, see WWDC 2014 video Improving Your App with Instruments).
As an aside, "garbage collection" refers to a very different memory system and isn't applicable here.
You can add deinit functions to your classes that will get called when your objects are deallocated.
If deinit isn't getting called, while your app is running, you can press the Debug Memory Graph button (circled below) and inspect what has a reference to what.
Use the dropdown menus at the top of the middle pane to toggle between classes and instances of classes.
If something is getting allocated over and over again without getting released you should see multiple instances, and you should be able to see via the directional graph if one of its children is holding a strong reference to its parent.
Use instruments to check for leaks and memory loss. Use Mark Generation (Heapshot) in the Allocations instrument on Instruments.
For HowTo use Heapshot to find memory creap, see: bbum blog
Basically the method is to run Instruments allocate tool, take a heapshot, run an iteration of your code and take another heapshot repeating 3 or 4 times. This will indicate memory that is allocated and not released during the iterations.
To figure out the results disclose to see the individual allocations.
If you need to see where retains, releases and autoreleases occur for an object use instruments:
Run in instruments, in Allocations set "Record reference counts" on (For Xcode 5 and lower you have to stop recording to set the option). Cause the app to run, stop recording, drill down and you will be able to see where all retains, releases and autoreleases occurred.
very simple approach is to put a print in deinitialiser
deinit {
print("<yourviewcontroller> destroyed.")
}
ensure that you are seeing this line getting printed on the console. put deinit in all your viewcontrollers. in case if you were not able to see for particular viewcontroller, means that their is a reference cycle.possible causes are delegate being strong, closures capturing the self,timers not invaidated,etc.
You can use Instruments to do that. As the last paragraph of this article states:
Once Instruments opens, you should start your application and do some interactions, specially in the areas or view controllers you want to test. Any detected leak will appear as a red line in the “Leaks” section. The assistant view includes an area where Instruments will show you the stack trace involved in the leak, giving you insights of where the problem could be and even allowing you to navigate directly to the offending code.

Xcode fix memory problems

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

Getting Low Memory Logs/Reports on iOS Programmatically

I am currently using PLCrashReporter to get the crash reports on ios device programmatically. According to https://developer.apple.com/library/ios/technotes/tn2151/_index.html , http://www.raywenderlich.com/23704/demystifying-ios-application-crash-logs Memory logs are different than crash logs. How can I get this memory logs programmatically ? Are there any libraries available ?
You need to use Instruments to determine why the memory is being exhausted. Use the Analyzer to check your code. Use Instruments Leaks tool.
Use instruments to check for leaks and memory loss due to retained but not leaked memory. The latter is unused memory that is still pointed to. Use Mark Generation (Heapshot) in the Allocations instrument on Instruments.
For HowTo use Heapshot to find memory creap, see: bbum blog
Basically the method is to run Instruments allocate tool, take a heapshot, run an iteration of your code and take another heapshot repeating 3 or 4 times. This will indicate memory that is allocated and not released during the iterations.
To figure out the results disclose to see the individual allocations.
If you need to see where retains, releases and autoreleases occur for an object use instruments:
Run in instruments, in Allocations set "Record reference counts" on (For Xcode 5 and lower you have to stop recording to set the option). Cause the app to run, stop recording, drill down and you will be able to see where all retains, releases and autoreleases occurred.
One way is to plug in the device to Xcode. Go to Window > Devices and Simulators, select your device, click "View Device logs" button. Low memory logs will be in there under process "Unknown" and type "Unknown".
See https://developer.apple.com/library/content/qa/qa1747/_index.html under "Getting Crash Logs and Console Output From a Device Using Xcode" section.

app works fine on iPad 2, crashes on iPad 3, with low memory warning

as the title says, I have an app which works on iPad 2, but crashes on iPad 3. when running it the console gives me a low memory warning message. When the crash happens I symbolicate it, but there's really nothing that I can relate to the code, like it shows
process name, UUID, rpages, recent_max, [reason] (state)
and under those column headers just hexadecimal stuff, nothing showing method calls or lines in the project.
Any ideas? am I missing some flags in the code that allows for a better crash log?
Thanks.
If you're getting low memory warnings and fail to release enough memory to resolve the issue, your app will almost certainly crash. The thing is, I don't think that the particulars of how or why it crashed can possibly be illuminating. At that point, you're evaluating secondary symptoms. You really need to go back and figure out why you got the low memory warning in the first place and fix that problem.
As Daniel said, you can look at Technical Note 2151, but as it says:
When you see a low memory crash, rather than be concerned about what part of your code was executing at the time of termination, you should investigate your memory usage patterns and your responses to low memory warnings. Memory Allocations Help lists detailed steps on how to use the Leaks Instrument to discover memory leaks, and how to use the Allocations Instrument's Mark Heap feature to avoid abandoned memory. Memory Usage Performance Guidelines discusses the proper ways to respond to low-memory notifications as well as many tips for using memory effectively. It is also recommended that you check out the WWDC 2010 session, Advanced Memory Analysis with Instruments.
So, a couple of thoughts:
Have you looked for leaks? The Finding Leaks article walks you through how to use instruments to find your leaks.
If you turned on zombies, have you turned them off? Zombies is a great diagnostic tool, but just consumes memory.
Have you run your code through the static analyzer (shift+command+B or select "Analyze" on the "Product" menu)? Especially if using non-ARC code, this can find lots of memory issues.
Have you examined your allocations for unexplained increases without offsetting decreases with the Instrument's Allocations tool. Using that, you can run the program, look at the consumption of memory on the graph and see if you see any increases that aren't offset at some point by the corresponding decreases. And if so, highlight those increases in the graph:
For example, when running the Allocations tool, hold down the option key and then click-and-drag with your mouse to highlight a portion of the timeline, to identify what you want to inspect. You probably want to focus on one of your spikes in allocations. For example, I found a bump in my allocations and highlighted it as such (this was a ludicrously simple example where I create a huge array in viewDidLoad, but hopefully it give you the idea):
Note, I find it useful to show the call tree in the lower panel, it's often useful to select "Hide System Libraries", to focus on your code (and "Invert Call Tree", too). And if you double click on the method name in Instruments (in my example, here, it would be viewDidLoad), Instruments will then show you your code that's doing the allocation:
Low memory warnings generate a different kind of log than standard crashes. Take a look at the "Understanding Low Memory Reports" section of this article to understand what happened with your application and how you can debug it using Instruments: http://developer.apple.com/library/ios/#technotes/tn2151/_index.html

Why do allocations continue to rise in Zombie Instruments tool when they don't in Allocations Instruments tool

I'm new to Xcode and iOS and have been experimenting today with the Instruments tool. There are some things I'm struggling to get my head around:
Scenario:
I have created a simple app that filters a UITableView with a UISearchBar (target iOS5 with ARC). Simple code, nothing too fancy.
For my own learning, I was watching the allocations as I perform various actions - in this particular instance when I'm typing in the search bar.
Using the 'Allocations' instrument tool, I get the following:
As expected, I see a sudden spike in memory allocated when I first start typing in the search bar. After that, any further searching makes no significant difference to the allocation graph.
However, when I look at the memory allocations using the 'Zombie' instrument tool, the allocation graph continues to rise whenever I type in the search bar.
At first, I thought this may have been to what I was tracking - and I tried to ensure all the settings were matched. However, it still shows a rising graph when searching.
Does anyone have an explanation of this? No doubt I have some conceptual misunderstanding about what the allocation tool is tracking in each of these instrument modes.
The Zombies instrument works by telling your app not to free objects. Instead, when the object would be deallocated, the app instead leaves the object allocated but changes the object's class to a special zombie class that handles any message by raising a zombie error.
Thus, allocations keep rising under the Zombie instrument because nothing is really deallocated.
Incidentally, since using Zombies prevents the app from deallocating objects, the app usually requires much more memory to run under the Zombies instrument. This is ok on a Mac (and in the iOS Simulator running on a Mac), since the Mac probably has several gigabytes of RAM and also supports paging to disk. But iOS devices only have between 256 MB and 1 GB of RAM, and don't support paging. This is probably why Instruments won't let you use Zombies on an iOS device.

Resources