Memory Debugging
Systematic memory leak diagnosis with Instruments and the Memory Graph Debugger. Most leaks come from forgetting to stop timers, observers, or subscriptions, and this skill covers those patterns and the rest.
When to Use
Use this skill when:
- App memory grows steadily during use (50MB → 100MB → 200MB) instead of leveling off
- Instruments shows several instances of the same view controller
- The app crashes after 10–15 minutes with no error message
- Instruments shows retain cycles or leaked objects
- View controllers don't deallocate after they're dismissed
Time investment Finding a leak with Instruments takes 5–15 minutes; guessing takes 45 minutes or more.
Example Prompts
Questions you can ask your assistant that will draw from this skill:
- "My app crashes after 10–15 minutes of use with no error messages. How do I find the leak?"
- "Memory jumps from 50MB to 200MB+ on a specific action. Is this a leak or normal caching?"
- "View controllers don't deallocate after dismiss. How do I find the retain cycle?"
- "I have timers and observers that might be leaking. How do I verify and fix them?"
- "My app uses 200MB. Is that normal, or do I have multiple leaks?"
- "How do I set up Instruments to track memory leaks?"
- "How do I verify my fix actually worked?"
What This Skill Provides
Common Leak Patterns
- Timer leaks (about 50% of leaks) – Repeating timers the run loop keeps alive;
[weak self]alone doesn't stop them, so invalidate them or use a Combine timer - Observer and notification leaks (about 25%) – Observers that are never removed
- Closure capture leaks (about 15%) –
selfcaptured strongly in stored closures and callback arrays - Strong reference cycles – Objects that hold each other through closures
- View and layout callback leaks – Closures that capture view controllers, where a weak delegate would do
- PhotoKit image request leaks –
PHImageManagerrequests that are never cancelled during scrolling
Diagnosis
- Mandatory first steps: check device logs, open the Memory Graph Debugger, and repeat the action while profiling
- Reading the shape of the memory line: flat, a steady climb, a spike that levels off, or stacked spikes (compound leaks)
- A three-phase workflow: confirm the leak, locate it, then fix and verify
- Intermittent leaks that disappear under Instruments, diagnosed with
deinitlogging and MetricKit - Command-line checks without Instruments:
leaks,heap,vmmap,stringdups,malloc_history, andxctrace
Measuring Peak and Resting Memory
- Reading
phys_footprintandledger_phys_footprint_peakfromtask_info(TASK_VM_INFO), plus your remaining headroom fromos_proc_available_memory() - Which number each termination is judged against: a memory-limit kill checks your instantaneous footprint, while background jetsam picks victims by their resting figure
- Why the peak never resets (read it before and after, and take the difference), why polling misses spikes, and where peaks hide (scrolling back, reopening a document, image pipelines)
Jetsam
- Clearing caches when the app moves to the background
- Restoring state so users don't notice a jetsam termination
- Tracking memory-pressure exits with MetricKit, and memory exception diagnostics with the call stack at a memory-limit kill (iOS 27)
The Swift 6.4 Capture Warning (Xcode 27)
- The
[#ImplicitStrongCapture]warning for a[weak self]inner closure inside an outer closure that already capturesselfstrongly, and how to fix it by intent rather than silencing it
Key Pattern
Closure Capture Fix
// ❌ LEAKS: Strong capture of self
viewModel.onUpdate = {
self.updateUI() // self captured strongly
}
// ✅ SAFE: Weak capture
viewModel.onUpdate = { [weak self] in
self?.updateUI()
}
// ✅ Verify fix worked
deinit {
print("ViewController deallocated") // Should print on dismiss
}Quick Diagnosis Checklist
- Does the class have a
deinit? Add one with a print statement - Does
deinitrun when expected? If not, there's a leak - Check closures, delegates, timers, and observers
- Use the Memory Graph Debugger to find what's holding the object
Documentation Scope
This page documents the memory-debugging skill in the axiom-performance suite. Your assistant uses its leak diagnosis workflows, Instruments setup, leak patterns, and peak-memory measurement when helping you debug memory problems.
Related
- /axiom:audit memory – A quick automated scan of your code for leak patterns
- memory-auditor – An agent that scans your whole codebase for leaks
- objc-block-retain-cycles – The same diagnosis for Objective-C blocks
- timer-patterns – Timer crash patterns and run loop mode problems
- lldb – Inspecting the objects you find at a breakpoint
- performance-profiling – Broader profiling, including CPU and energy
- metrickit-ref –
DiagnosticReport.memoryExceptionand MetricKit setup for memory-limit kills in the field
Resources
WWDC: 2021-10180, 2020-10078, 2018-416
Docs: /xcode/gathering-information-about-memory-use, /metrickit/mxbackgroundexitdata