Identifying memory leaks

I'm having an issue with my swiftui macOS application where it is continually consuming more memory over time and after a couple of hours will grind to a halt. I've watched a few videos now on how to use Xcode Memory Graph and Instruments to identify the source of a leak (I assume it is a leak). These videos all provide very obvious issues as examples but mine seems more elusive and I don't know how to identify which part of my code is the cause of the issue.

After running instruments I see the following but the leaked objects are not always consistent:

Xcode Memory Graph shows NSSet as the culprit which is shown under CoreFoundation (not my App). I really am a beginner here and because it's not showing me somewhere in my app that I can go and investigate I'm really stuck.

Your screenshot shows you have the Leaks track in Instruments selected, and a small number of leaks that are a handful of bytes in total. You should spend your time with the Allocations track above the Leaks track instead, to see what is allocating memory and then holding on to it for long periods of time, since the graph shows upward slopes, though we can't see the scale of memory use involved. That way, you are chasing down a memory consumption problem by focusing on the largest allocations of memory in your app, rather than leaks that are comparatively minuscule.

To learn how to use the Allocations features, Technote 2434 is a great resource.

—Ed Ford,  DTS Engineer

I agree with Ed, that the “Leaks” instrument is not as illuminating as the “Allocation” instrument. The “Leaks” instrument is most useful (in those edge-cases) where you are writing code that does manual alloc/free or retain/release. If not, it is generally identifying (usually very small) issues largely outside of your control. But if you don’t have your own code that is doing alloc/free or retain/release, the “Leaks” instrument is not terribly illuminating.

Your “Debug Memory Graph” diagnostics, though, are very effective in identifying strong reference cycles. But rather than focusing on the bottom of the tree (the fact that the NSSet was used deeply within Foundation types), I would focus on the top of that tree. E.g., it looks like the NSSet was created by some AVFoundation layer view. So, I would focus on the AVFoundation usage within the app.

Even better, in the left panel of the “Debug Memory Graph”, I would advise that we focus on our own objects, i.e., those objects over which we have control. To the right of the filter bar at the bottom of the panel on the left, there is a button that says “Show only content from workspace”: That’s a great way to focus on those objects over which you have control. See if there are any of your objects present that are still present that should no longer be there. E.g., you might have a view controller that shouldn’t be there (perhaps because of some strong reference cycle); and that view controller may lead to many objects (like this NSSet) not being released, as well. So rather than worrying about the NSSet, focus on the view controller (or which ever of your objects that should no longer be in memory). Bottom line, even though this big object was buried in some Foundation type, it often is a side-effect of some issue in the memory management of our own objects.

So, I might advise a “top down” focus on the objects in memory, rather than “bottom up”. A single top level object from your workspace that hasn’t been released will lead to a cacophony of lower-level objects, and there’s no reason to dwell on those lower-level objects until you’ve resolved your top-level objects that haven’t been released.

One last suggestion: When using “Debug Memory Graph”, sometimes it is not enough to know which type of object is keeping a strong reference to another, but rather you want help knowing where in your code these strong references are established. If you haven’t already, I would go to your scheme’s “Run” » “Diagnostics” options, and temporarily turn on “Malloc Stack Logging”. This way, when looking at the “Debug Memory Graph”, you can show the stack trace on the right, and it can help you navigate to the line of code that established a strong reference. Sometimes this isn’t necessary (as one can make educated guesses), but sometimes it can be incredibly useful if you cannot otherwise divine what you may have done to establish a strong reference cycle, or the like. Just remember to turn off this “Malloc Stack Logging” once you have identified and resolved your memory management issue.

Identifying memory leaks
 
 
Q