JVM Heap Dump 분석 과정 공유

janjanee·2024년 9월 4일
0

Java

목록 보기
19/19

서비스 중 JVM heap memory가 지속적으로 증가함에 따라 OOM이 발생했고, OOM 시 heap dump를 떠서 분석한 과정을 공유합니다.

정상 그래프

  • GC가 정상적으로 수행됨

비정상 그래프

  • GC가 수행됨에도 불가하고 지속적으로 그래프가 증가됨을 발견!

결국 OOM이 발생했고, 사전에 OOM 발생 시 heap dump를 남기는 JVM Option 추가 작업 및 k8s preStop hook으로 해당 heap dump 파일을 s3 경로로 copy하는 작업을 해두었다

선행 작업

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${heapDumpPath}
lifecycle:
   preStop:
      exec:
        command:
           - /bin/sh
           - '-c'
           - >
             POD_NAME=$(hostname)
             CURRENT_DATE=$(date +%Y-%m-%d)
             aws s3 cp /tmp/heapdumps/*.hprof
             s3://bucket/$CURRENT_DATE/$POD_NAME/

             sleep 10

Eclipse MAT Tool로 heapdump 파일 분석

  • Leak Suspects를 클릭한다

  • Problem Suspect 1이 거의 90퍼센트나 차지하고 있다.
  • 특정 클래스의 line에서 문제가 되는 부분을 알려준다.

문제 코드

@Slf4j
@Service
public class RedisMessageSubscriber implements MessageListener {

  public static List<String> messageList = new ArrayList<>();

  public void onMessage(final Message message, final byte[] pattern) {
    messageList.add(message.toString());
    log.debug("Message received: " + new String(message.getBody()));
  }
}
  • 해당 서비스는 Redis publish를 하고 subscribe 하는 부분은 없는데 테스트 목적으로 작업했던 코드가 문제를 일으켰다 (삭제했어야 하는 클래스이다 ! ! !)

  • 다른 서비스에서 publish한 메시지를 위의 리스너가 subscribe하면서 messageList.add를 하고 있던게 OOM 문제의 원인이었다.

    -> 해당 서비스와 연관없는 메시지를 list에 저장하고 있었다...

  • @Service로 등록된 객체는 Spring 컨테이너가 관리하므로, 컨테이너가 해당 객체를 참조하고 있는 동안에는 GC 대상이 되지 않는다.

  • static으로 선언된 변수나 객체는 GC의 대상이 아니다.

결론

  • 쓸데없는 코드가 들어가서 말썽을 부렸다. 코드리뷰를 더 잘하자!
  • 코드 정적 분석기를 도입하기로 검토 (만능은 아니지만 참고용으로)
profile
얍얍 개발 펀치

0개의 댓글