“썼으면 치워야지?”
메모리를 붙잡고 안 놔주는 코드들
메모리 릭이란?
메모리 릭은 더 이상 필요하지 않은 데이터가 메모리에 계속 남아있는 현상을 말합니다.
쉽게 말하면,
"쓰고 난 데이터를 해제하지 않아서, 메모리가 줄지 않고 쌓이는 것."
이건 일종의 메모리 낭비이자 성능 저하의 원인입니다.
왜 발생할까?
다음 중 하나라도 해당하면 메모리 릭이 생깁니다
- 객체에 대한 참조를 실수로 남겨둠
- 이벤트 리스너를 등록만 하고 해제하지 않음
- 클로저나 전역 변수로 인해 GC 대상에서 제외됨
- 사용이 끝난 리소스를 명시적으로 해제하지 않음 (특히 C, C++)
GC(Garbage Collector) 는 똑똑하지만, 모든 상황을 완벽히 처리하지는 못해요.
어떤 문제가 생기나?
메모리 점유율 증가
- 시간이 지날수록 앱이 점점 느려짐
- 모바일에선 앱이 강제 종료되기도 함
서버에서는 치명적인 리스크
- 장시간 실행되는 서비스에서 메모리 릭은 누적된 폭탄
- 결국 OOM(Out Of Memory) 에러로 서비스 다운
주요 언어에서의 메모리 릭 예시
JavaScript
let element = document.getElementById("btn");
element.addEventListener("click", () => {
console.log("clicked!");
});
// DOM에서 제거하지 않으면 메모리 릭 발생 가능
Java
List<Object> list = new ArrayList<>();
while (true) {
list.add(new Object()); // 계속 쌓이지만 제거 X → 메모리 릭
}
C
char* str = malloc(100);
// 사용 후 free(str); 하지 않으면 메모리 릭 발생
Python
class A:
def __init__(self):
self.ref = self
a = A()
del a
# 순환 참조 → 가비지 컬렉터가 회수하지 못함
진단과 해결 방법
모니터링 도구 사용
- 브라우저: Chrome DevTools → Memory 탭
- Node.js:
--inspect
로 실행 후 heap snapshot - Java: VisualVM, JProfiler
- Python:
objgraph
,gc
모듈 - C/C++: Valgrind
Heap Snapshot 비교
- 시간 간격을 두고 찍은 힙 스냅샷을 비교해
누가 계속 쌓이고 있는지 추적
레퍼런스 카운트 확인
- 객체가 어디서 참조되고 있는지 추적
- 예상치 못한 참조가 남아 있는지 확인
예방을 위한 습관
- 이벤트 리스너는 해제하자
→removeEventListener
필수 - 타이머는 클리어하자
→clearInterval
,clearTimeout
- 전역 변수 남용 금지
→ 클로저, 전역 상태 관리 주의 - 클래스 내부에 순환 참조 피하기
→ 한쪽 참조를 약한 참조(WeakRef, WeakMap 등)로 대체 - 리소스는 명시적으로 해제하자
→ DB 연결, 파일 핸들, 스트림 등은close()
필요
마무리
메모리 릭은 한 번에 확 터지진 않지만,
서서히 시스템을 갉아먹는 좀비 같은 존재입니다.
다행히도, 원리를 잘 이해하고 습관만 잘 들이면
얼마든지 예방할 수 있어요.
“눈에 안 보인다고, 없는 게 아니다.”
개발할 때 자원을 “언제, 어디서, 어떻게 해제할지” 항상 염두에 둬야 합니다.
특히 장기 실행되는 앱/서비스라면 더욱 주의하세요!
728x90