본문 바로가기
Dev

Java 힙 덤프 자동화: jcmd와 쉘 스크립트로 OOM 문제 진단하기

by 바람냄새 2025. 6. 26.

Java 애플리케이션 운영 중 OutOfMemoryError (OOM)와 같은 메모리 관련 이슈는 개발자와 운영자에게 큰 골칫거리입니다. 이런 문제를 진단하는 가장 효과적인 방법 중 하나는 특정 시점의 힙 메모리 상태를 담은 **힙 덤프(Heap Dump)**를 분석하는 것인데요. 수동으로 프로세스 ID(PID)를 찾아서 jcmd 명령어를 실행하는 것은 번거롭고, 특히 긴급한 상황에서는 더욱 그렇습니다.

오늘 이 글에서는 Java 애플리케이션의 힙 덤프 생성을 쉘 스크립트로 자동화하는 방법을 소개합니다. 이를 통해 빠르고 효율적으로 필요한 진단 정보를 확보하고 문제 해결 시간을 단축할 수 있습니다.


왜 힙 덤프 자동화가 필요한가요?

  • 신속한 대응: OOM과 같은 문제가 발생했을 때, 수동 개입 없이 즉시 힙 덤프를 생성하여 현상 발생 시점의 메모리 상태를 기록할 수 있습니다.
  • 반복적인 작업 제거: 여러 서버에서 동일한 작업을 반복할 필요 없이 스크립트 하나로 일관된 작업을 수행할 수 있습니다.
  • 정확성 향상: 수동 작업 시 발생할 수 있는 PID 오입력 등의 실수를 줄여줍니다.
  • 스케줄링 가능: 크론(cron) 같은 스케줄러와 연동하여 특정 시간에 주기적으로 힙 덤프를 생성하거나, 모니터링 시스템과 연동하여 특정 조건(예: 메모리 사용량 임계치 초과)에서 자동으로 힙 덤프를 뜨도록 설정할 수 있습니다.

쉘 스크립트로 힙 덤프 생성 자동화하기

아래는 특정 Java 애플리케이션(MYAPP_API.jar)의 PID를 찾아 힙 덤프를 생성하는 쉘 스크립트입니다.

 

create_heapdump.sh

 

 

Bash
 
#!/bin/bash

# 힙 덤프를 생성할 Java 애플리케이션의 고유한 이름 (명령줄에서 식별 가능한 부분)
APP_NAME="MYAPP_API.jar"

# --- 프로세스 ID (PID) 찾기 ---
# 'pgrep -f'를 사용하여 전체 명령줄에서 APP_NAME에 해당하는 PID를 찾습니다.
PID=$(pgrep -f "${APP_NAME}")

# PID를 찾지 못했다면 오류 메시지를 출력하고 종료합니다.
if [ -z "$PID" ]; then
    echo "오류: ${APP_NAME}에 해당하는 프로세스를 찾을 수 없습니다. 종료합니다."
    exit 1
fi

echo "${APP_NAME}에 해당하는 PID ${PID}를 찾았습니다."

# --- 힙 덤프 저장 경로 및 파일명 정의 ---
# 힙 덤프 파일을 저장할 디렉토리
HEAP_DUMP_DIR="/home/myapp/api/logs/heapdumps"
# 파일명에 포함될 현재 타임스탬프 (YYYYMMDD_HHMMSS 형식)
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
# 최종 힙 덤프 파일 경로 및 이름
HEAP_DUMP_FILE="${HEAP_DUMP_DIR}/heapdump_myapp_api_${TIMESTAMP}.hprof"

# 힙 덤프 디렉토리가 존재하지 않으면 생성합니다.
if [ ! -d "$HEAP_DUMP_DIR" ]; then
    echo "힙 덤프 디렉토리를 생성합니다: ${HEAP_DUMP_DIR}"
    mkdir -p "$HEAP_DUMP_DIR"
    # 디렉토리 생성 실패 시 종료
    if [ $? -ne 0 ]; then
        echo "오류: 디렉토리 ${HEAP_DUMP_DIR} 생성에 실패했습니다. 종료합니다."
        exit 1
    fi
fi

# --- jcmd 명령어를 사용하여 힙 덤프 생성 ---
echo "jcmd ${PID}를 사용하여 ${HEAP_DUMP_FILE}에 힙 덤프를 생성합니다..."
jcmd "${PID}" GC.heap_dump "${HEAP_DUMP_FILE}"

# jcmd 명령의 실행 결과 확인
if [ $? -eq 0 ]; then
    echo "성공적으로 힙 덤프를 생성했습니다: ${HEAP_DUMP_FILE}"
else
    echo "오류: PID ${PID}에 대한 힙 덤프 생성에 실패했습니다."
fi

exit 0

스크립트 사용 방법

  1. 스크립트 저장: 위의 내용을 create_heapdump.sh와 같은 파일 이름으로 저장합니다.
  2. 실행 권한 부여: 터미널에서 다음 명령어를 실행하여 스크립트에 실행 권한을 부여합니다.
    Bash
     
    chmod +x create_heapdump.sh
    
  3. 스크립트 실행: 다음 명령어로 스크립트를 실행합니다.
    Bash
     
    ./create_heapdump.sh
    

스크립트 주요 코드 설명

  • APP_NAME="MYAPP_API.jar": 힙 덤프를 뜰 Java 애플리케이션의 식별자입니다. ps -ef | grep java 명령 시 출력되는 결과에서 해당 애플리케이션을 유일하게 식별할 수 있는 문자열을 사용하세요. 여기서는 MYAPP_API.jar를 사용했습니다.
  • PID=$(pgrep -f "${APP_NAME}"): pgrep 명령어는 프로세스 이름이나 명령줄 패턴을 기반으로 PID를 찾아줍니다. -f 옵션은 전체 명령줄을 대상으로 검색하도록 하여 Java 애플리케이션의 경우 유용합니다.
  • HEAP_DUMP_DIR="...", TIMESTAMP=$(date +"%Y%m%d_%H%M%S"), HEAP_DUMP_FILE="...": 힙 덤프 파일이 저장될 경로와 파일명을 정의합니다. 파일명에 타임스탬프를 포함하여 각 덤프가 고유하도록 합니다.
  • mkdir -p "$HEAP_DUMP_DIR": 힙 덤프 저장 디렉토리가 없다면 생성합니다. -p 옵션은 상위 디렉토리까지 한 번에 생성해줍니다.
  • jcmd "${PID}" GC.heap_dump "${HEAP_DUMP_FILE}": 이 부분이 핵심입니다. jcmd는 JVM(Java Virtual Machine)에 명령을 보내는 도구입니다. 특정 PID에 대해 GC.heap_dump 명령을 실행하여 지정된 경로에 힙 덤프를 생성합니다.
  • if [ $? -eq 0 ]; then ... fi: 이전 명령어의 종료 상태를 확인합니다. $? 변수에는 마지막으로 실행된 명령어의 종료 코드가 저장되며, 0은 보통 성공을 의미합니다.

마무리하며

이 쉘 스크립트를 활용하면 Java 애플리케이션의 힙 덤프 생성을 간편하게 자동화할 수 있습니다. 이렇게 생성된 .hprof 파일을 Eclipse Memory Analyzer (MAT)VisualVM과 같은 도구로 분석하면 메모리 누수나 비효율적인 객체 사용 등을 효과적으로 진단하고 해결할 수 있을 거예요. 여러분의 Java 애플리케이션이 더욱 안정적으로 운영되기를 바랍니다!