How to Use the heap Command in macOS Terminal to Inspect Process Class Instances and Heap Allocations

When profiling memory performance or investigating subtle memory leaks in macOS applications, high-level utilities like Activity Monitor and top show only coarse figures such as resident memory size. While low-level tools like vmmap report how virtual memory is partitioned across general MALLOC zones, they cannot answer the most vital developer question: which specific object classes and data structures are accumulating on the heap?

To inspect the actual contents of dynamic heap memory, macOS includes a powerful developer CLI diagnostic tool: heap. Located in /usr/bin/heap, this binary inspects the Mach-O heap of any running Objective-C, Swift, or C++ process, generating granular breakdowns of every allocated class instance, reference count, and byte volume. This guide explains how to execute, interpret, and automate heap analysis on macOS.

What the heap Utility Analyzes

In macOS, object-oriented applications allocate memory dynamically on the heap through runtime allocators (malloc() in C, alloc in Objective-C, and class initialisation in Swift). The heap tool traverses these allocation zones and provides:

  • Class Instance Counts: The total number of living instances for every compiled class (e.g., NSString, NSData, custom Swift models).
  • Zone Aggregations: Total memory consumed by each individual class type across MALLOC_TINY, MALLOC_SMALL, and MALLOC_LARGE zones.
  • Object Address Enumeration: The physical 64-bit virtual memory addresses of individual class allocations.
  • Zone Fragmentation: Visibility into free blocks and memory fragmentation within heap regions.

Basic Syntax and Security Requirements

To run heap, supply the Process ID (PID) or process name of the target application:

heap <PID or Process Name>

Because inspecting another process’s heap involves attaching to its task port, macOS security policies (System Integrity Protection and Hardened Runtime) require root privileges via sudo:

sudo heap 1420

Filtering and Customising heap Output

1. Sorting by Total Memory Consumed

By default, heap outputs classes in alphabetical order. To immediately identify which objects are causing memory bloat, pass the -s (sort) flag with bytes:

sudo heap -s bytes Safari | head -n 30

This command lists the heaviest classes at the top of the report, instantly pinpointing whether image buffers, JSON models, or string arrays dominate memory.

2. Filtering for Specific Application Classes

System frameworks often allocate thousands of baseline objects (such as OS_dispatch_queue or CFString). To filter for your application’s proprietary class prefix, pipe the output through grep:

sudo heap 28410 | grep "MyCompany"

3. Listing Individual Object Memory Addresses

To print the exact hex memory address of every allocated instance of a specific class (which can subsequently be inspected in lldb or passed to malloc_history):

sudo heap -addresses "NSData" 1420

Interpreting heap Summary Output

A typical heap execution generates a structured table listing class allocations across all MALLOC zones:

Output Column Explanation Diagnostic Significance
Class Name The registered Objective-C, Swift, or C++ class identifier Identifies the exact code abstraction occupying memory
Count Total number of living, uncollected instances in the heap An escalating count across user actions indicates a retain cycle
Bytes Total cumulative byte footprint of all instances of this class Highlights the highest-impact targets for memory optimisation
Average Size Average byte payload per instance (Bytes / Count) Helps distinguish between small object leaks and giant payload buffers

Real-World Example: Diagnosing a Retain Cycle

Suppose you are debugging a macOS document editing application. Users report that closing documents fails to release RAM. Run heap before and after opening and closing a document 5 times:

$ sudo heap -s count TextEditorPro | head -n 15
Class Name                                  Count        Bytes    Avg Size
========================================   ======      =======    ========
TEPDocumentViewController                     5        40,960       8,192
TEPUndoManager                                5        12,800       2,560
TEPTextStorage                                5       163,840      32,768
NSString                                  12,480       499,200          40

Notice that exactly 5 instances of TEPDocumentViewController remain resident in the heap despite all 5 documents being closed by the user! This clearly proves that strong reference cycles (such as un-invalidated delegates, notification observers, or timer blocks) are preventing the view controllers from deallocating.

Integrating heap with Other macOS Diagnostic Tools

  • malloc_history: Take an object address from heap -addresses <Class> <PID> and pass it to malloc_history <PID> <Address> to view the full call stack that initially allocated that object.
  • leaks: Run leaks <PID> to scan for orphaned heap allocations with no active incoming references.
  • vmmap: Run vmmap -summary <PID> to verify whether heap growth correlates with physical dirty page increases.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.