Java 메모리 누수 추적 플레이북
대부분의 Java 메모리 문제는 갑작스러운 크래시로 시작되지 않습니다. 릴리스가 반복될수록 재시작이 잦아지고, pause가 길어지고, 여유 메모리가 서서히 사라지는 형태로 시작됩니다.
먼저 증가 패턴을 분류해야 한다
누수라고 단정하기 전에 먼저 봐야 할 질문이 있습니다.
- GC 이후에도 heap 사용량이 돌아오지 않는가
- 메모리 증가는 트래픽 패턴 때문인가, 배포 이후 누적되는가
- off-heap buffer나 thread stack이 원인일 가능성은 없는가
첫 단계는 추측이 아니라 분류입니다.
흔한 누수 원인
- eviction 없는 in-memory cache
- 해제되지 않는 listener와 callback
- static 참조에 저장된 request context
- 오래 사는 singleton 객체에 붙은 대형 collection
대개 언어의 결함이라기보다 생명주기 설계 문제입니다.
dump에서는 retention 소유자를 찾아야 한다
Heap dump가 중요한 이유는 어떤 객체가 아직도 강한 참조로 살아 있는지 보여주기 때문입니다. 볼 포인트는 다음과 같습니다.
- dominator tree 크기
- 비정상적으로 큰 collection
- 재배포 후에도 남아 있는 classloader
- 큰 string, byte array, serialization buffer
중요한 질문은 “무슨 객체가 큰가”가 아니라 “왜 아직도 이 객체를 붙잡고 있는가”입니다.
런타임 메트릭과 코드 경계를 같이 봐야 한다
메모리 디버깅은 그래프와 릴리스를 연결할 때 빨라집니다. 예를 들면:
- old gen 점유율
- allocation rate 급증 구간
- full GC 빈도
- 증가와 상관관계가 있는 endpoint나 background job
이렇게 해야 dump를 열기 전에 후보군을 좁힐 수 있습니다.
예방 규칙이 더 중요하다
좋은 팀은 다음 같은 가드레일을 둡니다.
- eviction 있는 bounded cache
- 명시적인 lifecycle cleanup
- 지연시간뿐 아니라 메모리 추세를 보는 부하 테스트
- 릴리스별 heap 추세 비교 대시보드
최고의 누수 분석은 한 번의 영웅적 디버깅으로 끝나지 않고, 같은 실패가 다시 오지 않게 만드는 설계 규칙으로 이어집니다.
Continue Reading
다음으로 읽기 좋은 글
Java 개발자를 위한 Kotlin 실전 가이드
Kotlin을 Java의 짧은 대체 문법이 아니라, null safety, 상태 모델링, coroutine 구조까지 바꾸는 언어로 보고 실무 기준으로 정리합니다.
💬 LanguageJava 21 Virtual Threads 가상 스레드 실전 가이드
Java 21 Virtual Threads를 실서비스에 적용할 때 무엇이 좋아지고 무엇이 그대로 남는지, Spring Boot 환경에서 무엇을 먼저 검증해야 하는지 정리합니다.
📈 최신 동향JDK 25 최신 동향: LTS 채택을 어떻게 읽어야 하는가
JDK 25는 2025년 9월 16일 GA가 되었고 Java 25의 기준 구현입니다. 지금 중요한 것은 JEP 개수보다, 어떤 기능을 실전 채택 대상으로 보고 어떤 것은 관망해야 하는지입니다.
🖥️ FrontendCore Web Vitals 최적화 — LCP, CLS, INP 실전 가이드
Core Web Vitals를 체크리스트 수준이 아니라 사용자 체감 성능과 렌더링 구조의 관점에서 정리합니다. LCP, CLS, INP가 왜 나빠지는지, 무엇부터 측정하고 어떤 순서로 최적화해야 하는지 실무 예제로 설명합니다.
다음 탐색