Java 트러블슈팅 가이드: OOM, 스레드풀, 런타임 압박을 어디부터 볼까
마지막 업데이트

Java 트러블슈팅 가이드: OOM, 스레드풀, 런타임 압박을 어디부터 볼까


Java 장애는 JVM 옵션 하나를 더 잘 아는 것보다, 지금 눈앞의 문제가 메모리 압박인지, queue backlog인지, thread contention인지, 혹은 crash 전부터 누적된 saturation인지 먼저 가르는 쪽이 더 중요합니다.

이 글은 Java 운영 장애를 증상 기준으로 빠르게 분기하기 위한 허브입니다. 아래 내용을 다룹니다.

  • 지금 보이는 문제가 OOM 축인지, executor backlog 축인지, GC/CPU 축인지
  • 첫 몇 분 안에 무엇을 보면 다음 글을 빨리 고를 수 있는지
  • Java 장애에서 자주 하는 잘못된 출발은 무엇인지

결론부터 말하면 Java 장애는 “가장 먼저 크게 보이는 병목”을 기준으로 분기를 잡는 편이 가장 빠릅니다.


이 허브가 필요한 순간

  • OutOfMemoryError가 보인다
  • thread pool queue가 계속 증가한다
  • GC pause가 갑자기 길어졌다
  • CPU는 뜨거운데 throughput은 떨어진다
  • thread dump에서 waiting cycle이나 blocked progress가 보인다
  • 서비스가 완전히 죽기 전부터 이미 saturation 상태다

이 단계에서는 해결책보다 라우팅이 먼저입니다. 잘못된 레이어를 먼저 손대면 장애만 더 길어질 수 있습니다.

첫 5분 체크

jcmd <pid> GC.heap_info
jcmd <pid> Thread.print
jcmd <pid> VM.native_memory summary

VM.native_memory summary는 NMT가 켜져 있을 때 특히 유용합니다. 세 명령의 목적은 장애 전체 해결이 아니라 첫 분기 선택입니다.

  • heap과 영역 압박이 먼저 보인다: 메모리 축
  • queue보다 blocked thread와 waiting cycle이 더 선명하다: deadlock 또는 contention 축
  • CPU hot thread와 executor 포화가 먼저 보인다: backlog 또는 CPU 축

먼저 묻는 질문

1. 충돌하고 있는가, 멈추고 있는가, 버티고 있는가

crash, stall, saturation은 비슷해 보여도 첫 진단이 다릅니다. OOM이면 메모리 축이 먼저고, forward progress가 멈추면 deadlock 쪽이 먼저입니다.

2. 문제의 중심이 heap인가, queue인가

메모리가 크다고 해서 항상 메모리 문제인 것은 아닙니다. executor backlog가 먼저 커지면 메모리도 같이 오르기 쉽습니다.

3. pause가 핵심인가, contention이 핵심인가

긴 GC pause와 hot CPU는 모두 처리량 저하를 만들지만, 조사 방향은 다릅니다. pause가 먼저면 메모리 흐름을, contention이 먼저면 hot thread와 lock 대기를 봐야 합니다.

메모리 압박 축으로 들어가야 할 때

아래에 가깝다면 힙 덤프를 추출하여 OOM 발생 위치부터 파악하는 편이 좋습니다.

  • heap, metaspace, native 영역 압박이 보인다
  • OOM variant 자체가 중요하다
  • 큰 collection, cache, retained payload가 의심된다
  • class metadata 증가가 이상하다

retained object의 정체가 아직 모호하다면 힙 덤프 분석 도구를 이용해 참조 트리를 확인하는 것이 좋습니다.

queue backlog 또는 executor saturation 축으로 들어가야 할 때

아래에 가깝다면 스레드 풀 크기와 태스크 큐 설정을 다시 점검해 보세요.

  • executor queue가 계속 증가한다
  • worker가 계속 바쁘거나 blocked 상태다
  • 메모리보다 backlog가 더 먼저 눈에 띈다

함께 보면 좋은 글:

이 축은 “들어온 일을 왜 제때 소화하지 못하는가”를 좁혀 가는 문제에 가깝습니다.

긴 GC pause 또는 retained heap 축으로 들어가야 할 때

아래에 가깝다면 Java GC Pauses Too Long부터 보는 편이 좋습니다.

  • 명확한 crash보다 pause spike가 먼저 보인다
  • 트래픽이나 payload 변화 뒤 allocation churn이 커졌다
  • old generation 증가가 의심된다

pause보다 retained object 증거가 더 필요하다면 힙 덤프를 통해 수명이 긴 객체들을 확인해 보세요.

hot CPU 또는 contention 축으로 들어가야 할 때

아래에 가깝다면 스레드 덤프를 추출해 핫 스레드(Hot Thread)가 어디서 시간을 쓰는지 확인해 보세요.

  • CPU는 계속 높은데 throughput이 떨어진다
  • host metric보다 hot thread가 더 중요해 보인다
  • retry, spinning, contention, wasted work가 의심된다

lock contention이 특히 선명하면 병목이 되는 락(Lock) 획득 지점을 찾아 동시성 구조를 개선해야 합니다.

deadlock 또는 진행 정지 축으로 들어가야 할 때

서비스가 단순히 느린 것이 아니라 멈춘 것 같다면 락 순서 역전(Lock Ordering)이나 교착 상태(Deadlock)를 의심해 보는 편이 좋습니다.

  • thread dump에 waiting cycle이 보인다
  • throughput보다 lock ownership이 더 중요하다
  • 서비스가 forward progress를 거의 못 만든다

이 경우는 CPU 튜닝보다 진행 정지를 푸는 것이 먼저입니다.

자주 하는 오진

backlog가 원인인데 heap만 먼저 늘리는 경우

queue가 계속 쌓이는 상황에서 heap만 늘리면 장애가 덜 보일 뿐, 병목은 그대로 남습니다.

pause 문제를 무조건 CPU 문제로 보는 경우

처리량 저하가 보인다고 해서 항상 hot thread가 먼저는 아닙니다. GC pause가 긴 경우도 체감상 비슷하게 보입니다.

deadlock 신호를 놓치고 executor 설정만 만지는 경우

waiting cycle이 보이는데 pool 크기만 바꾸면 증상은 더 복잡해질 수 있습니다.

아주 빠른 분기표

  • OOM이나 메모리 영역 압박이 제일 선명하다: 메모리 글부터
  • queue 증가가 제일 선명하다: thread-pool backlog 글부터
  • pause spike가 먼저 보인다: GC 글부터
  • hot thread 또는 contention이 먼저 보인다: CPU 글부터
  • 진행 정지가 먼저 보인다: deadlock 글부터

그리고 첫 글 하나만 읽지 말고, 바로 인접 분기 하나를 더 비교하세요. Java 장애는 backlog와 메모리, contention과 deadlock이 서로 얽혀 보이는 경우가 많습니다.

장애 조사 순서

  1. 가장 먼저 크게 보이는 병목을 적습니다.
  2. 시스템이 crashing인지, stalling인지, saturating인지 구분합니다.
  3. memory-area pressure와 backlog pressure를 나눕니다.
  4. hot CPU contention과 blocked-progress deadlock을 나눕니다.
  5. 가장 강한 증상에 맞는 좁은 글로 들어간 뒤, 인접 분기 글 하나를 바로 비교합니다.

FAQ

Q. 이 글은 JVM 튜닝 가이드인가요?

아니요. 증상 기준으로 다음 분기를 고르는 허브 글입니다.

Q. queue 증가와 메모리 압박이 같이 보이면 어디부터 볼까요?

먼저 나타난 증상부터 보세요. 그리고 바로 이어지는 인접 가이드를 비교하면 됩니다.

Q. OOM은 없는데 GC pause만 심해졌다면 메모리 문제는 아닌가요?

그래도 메모리 압박 분기로 보는 것이 맞습니다. GC pause부터 보고, 필요하면 heap dump 분석으로 넘어가면 됩니다.

Q. CPU보다 deadlock을 먼저 봐야 하는 시점은 언제인가요?

forward progress가 멈추고, thread dump가 단순 hot loop보다 waiting cycle을 가리킬 때입니다.

  • 메모리 압박이 가장 선명하다면 힙 덤프를 통해 OOM 원인을 먼저 파악하세요.
  • queue backlog가 먼저 눈에 띄거나 스레드 풀 고갈이 의심되면 Java ForkJoinPool Starvation 사례를 참고해 보세요.
  • pause spike가 crash보다 더 눈에 띄면 Java GC Pauses Too Long를 보세요.
  • hot thread나 contention이 핵심이면 스레드 덤프로 병목 지점을 찾으세요.

먼저 읽어볼 가이드

검색 유입이 많은 핵심 글부터 이어서 보세요.

광고