Back to Blog
debugging memory leaks performance macOS

How to Debug Memory Leaks on Mac Using ProcXray

Step-by-step guide to finding and fixing memory leaks on macOS. Use ProcXray's real-time memory charts and process inspection to identify the root cause fast.

Memory leaks are among the most frustrating bugs to diagnose: your app is slow, your fan is spinning, and you’re not sure which process is the culprit or why. This guide walks through a systematic approach to finding memory leaks on macOS using ProcXray.

Recognizing the Symptoms

Before reaching for a tool, confirm you have a memory leak rather than legitimate high usage:

Step 1: Identify the Leaking Process

Open ProcXray and sort by Memory (descending). Look for:

  1. A process whose memory column keeps climbing
  2. A process with unexpectedly high memory for what it does (e.g., a background daemon using 2 GB)

ProcXray’s real-time memory chart in the sidebar updates continuously. Pin a suspect process and watch the trend line — a leak shows as a steady upward slope that never flattens.

Step 2: Check What the Process Actually Is

Click the suspect process. In the General tab you’ll find:

A background node process eating 3 GB is worth nothing without knowing it’s node /Users/me/myapp/server.js.

Step 3: Inspect Environment Variables

Switch to the Environment tab. Memory leaks in Node.js, Python, or Ruby apps are sometimes caused by environment misconfiguration:

Copy the environment as JSON and review any cache sizes, pool limits, or debug flags.

Step 4: Check Open File Descriptors

Memory leaks in servers are often paired with file descriptor leaks — the process opens files, sockets, or pipes and never closes them. Switch to the Connections tab in ProcXray to see:

A server that has 10,000 open file descriptors when it should have 50 is a strong signal of a related resource leak.

Step 5: Check Loaded Libraries

Sometimes the leak is in a native library. ProcXray’s Dylibs tab lists every dynamic library the process has loaded. Cross-reference this with your app’s known dependencies — an unexpected library version or a library that shouldn’t be there at all can explain unusual behavior.

Step 6: Reproduce and Confirm

Once you have a suspect (a specific process + code path), reproduce the leak deliberately:

  1. Note the process’s current memory in ProcXray
  2. Trigger the operation you suspect causes the leak (e.g., upload a file, run a query, call an API)
  3. Watch the memory trend in ProcXray
  4. Wait for the app to become idle
  5. If memory doesn’t return to baseline, you’ve confirmed the leak path

Step 7: Deep Dive with Platform Tools

ProcXray gives you the what and where. For the why inside your code, use platform-specific tools:

For native Swift/Objective-C apps:

leaks <PID>          # Apple's built-in leak detector
malloc_history <PID> # Allocation history

Or use Xcode’s Memory Graph Debugger for a visual call-tree.

For Node.js:

node --inspect server.js
# Then use Chrome DevTools Memory tab or clinic.js

For Python:

from memory_profiler import profile
@profile
def my_function():
    ...

Common Culprits

CauseSignal
Unbounded cacheMemory grows proportional to requests
Event listener not removedMemory grows with UI interactions
Native library leakDylibs tab shows version mismatch
File descriptors not closedConnections tab shows thousands of open files
Retained DOM nodesBrowser process grows despite navigating away

Summary

  1. Sort by memory in ProcXray and identify the growing process
  2. Use the General tab to confirm what the process is and how it was launched
  3. Check environment variables for misconfiguration
  4. Check the Connections tab for file descriptor leaks
  5. Reproduce the leak path
  6. Hand off to Xcode Memory Debugger, clinic.js, or memory_profiler for code-level analysis

Download ProcXray → to start your investigation — free, macOS Sonoma+.