Which command-line tools inspect a running process’s memory on macOS: leaks, heap, footprint and vmmap?
Predict first
What do the four tools say about this process?
import Foundation
final class N { var other: N?; var pad = [UInt8](repeating: 0, count: 1000) }
final class Held { var blob = [UInt8](repeating: 1, count: 4_000_000) }
final class Item { var x = 0 }
nonisolated(unsafe) var global: Held? = Held()
nonisolated(unsafe) var many: [Item] = (0..<500).map { _ in Item() }
func leakCycle() { let a = N(), b = N(); a.other = b; b.other = a }
leakCycle()
func tool(_ args: [String]) -> String {
let p = Process(); p.executableURL = URL(fileURLWithPath: args[0]); p.arguments = Array(args.dropFirst())
let pipe = Pipe(); p.standardOutput = pipe; p.standardError = Pipe()
try! p.run(); let d = pipe.fileHandleForReading.readDataToEndOfFile(); p.waitUntilExit()
return String(decoding: d, as: UTF8.self)
}
let pid = String(getpid())
let l = tool(["/usr/bin/leaks", pid])
print("leaks finds the cycle of N:", l.contains("ROOT CYCLE") && l.contains("<N "))
print("leaks reports the reachable global Held:", l.contains("Held"))
let h = tool(["/usr/bin/heap", pid])
let line = h.split(separator: "\n").first { $0.contains("Item") }.map { $0.split(separator: " ").prefix(2).joined(separator: " ") }
print("heap: Item instances and bytes:", line ?? "none")
print("footprint lists Malloc Large:", tool(["/usr/bin/footprint", pid]).contains("Malloc Large"))
print("vmmap has a physical footprint line:", tool(["/usr/bin/vmmap", "-summary", pid]).contains("Physical footprint:"))
- leaks finds the cycle true, reports Held false, heap: Item 500 16000, footprint lists Malloc Large true, vmmap physical footprint true
- leaks reports Held as a leak
- heap shows no Item instances
Mental model. leaks is the lost-property office, heap an inventory by item type, footprint the bill, and vmmap the floor plan of the warehouse.
Instruments is the friendly front end, but every view is also a command that you can script in CI or run on a Mac app or a simulator process (xcrun simctl spawn and then the same tools on the host pid). leaks <pid> scans the process and reports allocations unreachable from any root, grouped as ROOT CYCLE blocks with the reference chain (the check finds the cycle of N objects), and it does not report a 4 MB object held by a global: reachable memory is not a leak, which is exactly the leak versus abandoned distinction of Q158. heap <pid> lists live instances by class with counts and bytes (500 Items of 32 bytes: 500 16000), so you can compare two snapshots. footprint <pid> breaks the process’s dirty, clean and swapped memory down by category (Malloc Large, Malloc Metadata, __DATA_DIRTY), the number the system actually uses to decide whether you are too big (Q162). vmmap -summary <pid> shows the virtual memory regions and a physical footprint line.
Workflow: take footprint first to find the category that grows, heap to name the classes in it, leaks to see whether they are unreachable, then use the Memory Graph debugger for the retain chain (Q159). Details to remember: leaks needs the target to run under the same user and may need MallocStackLogging=1 to show allocation backtraces; sandboxed or hardened processes may refuse inspection; and results include system-library noise, so judge by your own classes. Exact output formats change between macOS releases, so the check parses only a few stable pieces.
leaks reports unreachable memory only; heap, footprint and vmmap show what is live and resident.
Common traps
- Reading every
leaksentry as your bug. Many are framework-internal; look for your own types. - Using
vmmapvirtual size as “how much memory the app uses”. The footprint number counts what is actually resident or compressed.
They might follow up with
- How do I run these on a device? Instruments and Xcode’s memory report; the command-line tools target Mac and simulator processes.
- Can I fail CI on leaks? Yes, run the tests and check that the
leakssummary reports 0 leaks for your types, with allowance for system noise.
Related questions
- 3.7.1 What is the difference between a leak, abandoned memory, fragmentation and a memory spike?
- 3.7.2 How do you find a retain cycle with the Memory Graph debugger?
- 3.7.3 How do you use Instruments’ Allocations, Leaks and VM Tracker to track a memory problem?
- 3.7.5 What is memory footprint, and what gets an iOS app killed?
← How do Array capacity and growth work, and when does an array give…