메모리 누수는 진단하기 가장 답답한 버그 중 하나입니다. 앱이 느려지고, 팬이 돌아가지만, 어느 프로세스가 범인인지 또는 왜 그런지 확신할 수 없습니다. 이 가이드는 ProcXray를 사용해 macOS에서 메모리 누수를 찾는 체계적인 접근법을 안내합니다.
빠른 답변
macOS에서 메모리 누수를 디버깅하려면, 먼저 회복되지 않고 꾸준히 증가하는 메모리 증가를 확인하고, 누수가 발생하는 프로세스를 식별한 다음, 실행 컨텍스트와 환경을 검증하고, 파일 디스크립터 또는 라이브러리 신호와 연관 지어 보세요. 빠른 분류에는 ProcXray를, 코드 수준의 근본 원인 분석에는 플랫폼별 프로파일러를 사용하세요.
증상 알아차리기
도구를 꺼내기 전에, 정당한 높은 사용량이 아니라 실제로 메모리 누수가 있는지 확인하세요:
- 시간이 지남에 따라 꾸준히 증가하는 메모리 (작업 중 일시적인 급증이 아님)
- 앱이 유휴 상태가 된 후에도 메모리가 줄어들지 않음
- 다른 앱을 닫았는데도 시스템 스왑 사용량이 증가함
- 증가를 유발한 작업이 끝난 후에도 프로세스가 메모리를 절대 해제하지 않음
1단계: 누수가 발생하는 프로세스 식별하기
ProcXray를 열고 Memory 기준(내림차순)으로 정렬하세요. 다음을 찾아보세요:
- 메모리 열이 계속 올라가는 프로세스
- 하는 일에 비해 예상외로 메모리를 많이 쓰는 프로세스(예: 2GB를 사용하는 백그라운드 데몬)
세부 정보 패널의 History 탭에 있는 ProcXray의 실시간 메모리 차트는 계속 업데이트됩니다. 의심스러운 프로세스를 고정하고 추세선을 지켜보세요. 누수는 결코 평평해지지 않고 꾸준히 상승하는 기울기로 나타납니다.
2단계: 프로세스가 실제로 무엇인지 확인하기
의심스러운 프로세스를 클릭하세요. General 탭에서 다음을 찾을 수 있습니다:
- 전체 실행 파일 경로(
node나python3처럼 프로세스 이름이 모호할 때 유용함) - 명령줄 인수(어떤 스크립트나 서버가 실행 중인지 알려줌)
- 작업 디렉터리
- 부모 프로세스(무엇이 이를 실행했는지 알려줌)
3GB를 잡아먹는 백그라운드 node 프로세스는 그것이 node /Users/me/myapp/server.js라는 사실을 모르면 아무 의미가 없습니다.
3단계: 환경 변수 검사하기
Environment 탭으로 전환하세요. Node.js, Python, Ruby 앱의 메모리 누수는 때때로 환경 설정 오류로 인해 발생합니다:
NODE_ENV=development가 프로덕션에서 무거운 디버그 미들웨어를 활성화함- 상세 로깅으로 설정되어 쿼리 객체를 메모리에 유지하는 ORM
- 제거(eviction) 정책 없이 설정된 캐시 라이브러리
환경을 JSON으로 복사해 캐시 크기, 풀 한도, 디버그 플래그를 검토하세요.
4단계: 열린 파일 디스크립터 확인하기
서버의 메모리 누수는 종종 파일 디스크립터 누수와 짝을 이룹니다. 프로세스가 파일, 소켓, 파이프를 열고 절대 닫지 않는 것이죠. ProcXray의 Connections 탭으로 전환해 다음을 확인하세요:
- 열린 모든 파일
- 수신 대기 중인 모든 포트
- 활성화된 모든 네트워크 연결
50개만 있어야 할 서버가 10,000개의 열린 파일 디스크립터를 가지고 있다면, 이는 관련 리소스 누수의 강력한 신호입니다.
5단계: 로드된 라이브러리 확인하기
때때로 누수는 네이티브 라이브러리에 있습니다. ProcXray의 Modules 탭은 프로세스가 로드한 모든 동적 라이브러리를 나열합니다. 이를 앱의 알려진 의존성과 대조하세요. 예상치 못한 라이브러리 버전이나 아예 있어서는 안 될 라이브러리가 비정상적인 동작을 설명할 수 있습니다.
6단계: 재현하고 확인하기
용의자(특정 프로세스 + 코드 경로)를 찾았다면, 의도적으로 누수를 재현하세요:
- ProcXray에서 프로세스의 현재 메모리를 기록하세요
- 누수를 일으킨다고 의심되는 작업을 실행하세요(예: 파일 업로드, 쿼리 실행, API 호출)
- ProcXray에서 메모리 추세를 지켜보세요
- 앱이 유휴 상태가 될 때까지 기다리세요
- 메모리가 기준선으로 돌아오지 않으면, 누수 경로를 확인한 것입니다
7단계: 플랫폼 도구로 심층 분석하기
ProcXray는 무엇이 그리고 어디서 를 알려줍니다. 코드 내부의 왜 를 알려면 플랫폼별 도구를 사용하세요:
네이티브 Swift/Objective-C 앱의 경우:
leaks <PID> # Apple's built-in leak detector
malloc_history <PID> # Allocation history
또는 시각적인 호출 트리를 보려면 Xcode의 Memory Graph Debugger를 사용하세요.
Node.js의 경우:
node --inspect server.js
# Then use Chrome DevTools Memory tab or clinic.js
Python의 경우:
from memory_profiler import profile
@profile
def my_function():
...
흔한 원인
| 원인 | 신호 |
|---|---|
| 무제한 캐시 | 메모리가 요청 수에 비례해 증가함 |
| 제거되지 않은 이벤트 리스너 | UI 상호작용에 따라 메모리가 증가함 |
| 네이티브 라이브러리 누수 | Modules 탭에 버전 불일치가 표시됨 |
| 닫히지 않은 파일 디스크립터 | Connections 탭에 수천 개의 열린 파일이 표시됨 |
| 유지되는 DOM 노드 | 다른 페이지로 이동해도 브라우저 프로세스가 증가함 |
요약
- ProcXray에서 메모리 기준으로 정렬하고 증가하는 프로세스를 식별하세요
- General 탭으로 프로세스가 무엇이고 어떻게 실행되었는지 확인하세요
- 환경 변수에 설정 오류가 없는지 확인하세요
- Connections 탭에서 파일 디스크립터 누수를 확인하세요
- 누수 경로를 재현하세요
- 코드 수준 분석을 위해 Xcode Memory Debugger, clinic.js, 또는 memory_profiler로 넘기세요
ProcXray 다운로드 → 후 조사를 시작하세요 — 무료, macOS Sonoma+.
자주 묻는 질문
메모리 누수와 정상적인 메모리 급증을 어떻게 구분하나요?
급증은 작업 부하가 끝난 후 안정되거나 떨어지는 경우가 많습니다. 누수는 일반적으로 반복된 작업에 걸쳐 지속적으로 증가하며, 유휴 기간에도 기준선으로 돌아오지 않는 형태로 나타납니다.
메모리 누수를 디버깅할 때 왜 파일 디스크립터를 확인하나요?
리소스 누수는 함께 발생하는 경우가 많습니다. 소켓이나 파일을 계속 열면서 닫지 않는 프로세스는 디스크립터 증가와 메모리 압박을 모두 보일 수 있습니다.
누수가 발생하는 프로세스를 찾은 후에는 어떤 도구를 사용해야 하나요?
언어별 또는 플랫폼별 프로파일러를 사용하세요. Swift/Objective-C에는 Xcode Memory Graph, Node.js에는 DevTools/clinic.js, Python에는 memory_profiler 또는 tracemalloc 워크플로를 사용합니다.