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:
- Steadily increasing memory over time (not just a spike during a task)
- Memory doesn’t drop after the app becomes idle
- System swap usage grows even though you’ve closed other apps
- The process never frees memory even after the work that triggered growth is complete
Step 1: Identify the Leaking Process
Open ProcXray and sort by Memory (descending). Look for:
- A process whose memory column keeps climbing
- 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:
- Full executable path (useful when the process name is ambiguous, like
nodeorpython3) - Command-line arguments (tells you which script or server is running)
- Working directory
- Parent process (tells you what launched it)
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:
NODE_ENV=developmentenabling heavy debug middleware in production- An ORM configured for verbose logging that keeps query objects in memory
- A cache library configured with no eviction policy
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:
- All open files
- All listening ports
- All active network connections
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:
- Note the process’s current memory in ProcXray
- Trigger the operation you suspect causes the leak (e.g., upload a file, run a query, call an API)
- Watch the memory trend in ProcXray
- Wait for the app to become idle
- 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
| Cause | Signal |
|---|---|
| Unbounded cache | Memory grows proportional to requests |
| Event listener not removed | Memory grows with UI interactions |
| Native library leak | Dylibs tab shows version mismatch |
| File descriptors not closed | Connections tab shows thousands of open files |
| Retained DOM nodes | Browser process grows despite navigating away |
Summary
- Sort by memory in ProcXray and identify the growing process
- Use the General tab to confirm what the process is and how it was launched
- Check environment variables for misconfiguration
- Check the Connections tab for file descriptor leaks
- Reproduce the leak path
- Hand off to Xcode Memory Debugger, clinic.js, or memory_profiler for code-level analysis
Download ProcXray → to start your investigation — free, macOS Sonoma+.