CodeGym /행동 /JAVA 25 SELF /메모리 분석 도구: jmap, jvisualvm

메모리 분석 도구: jmap, jvisualvm

JAVA 25 SELF
레벨 64 , 레슨 3
사용 가능

1. jmap: 명령줄의 마법

자동 가비지 컬렉터는 물론 훌륭합니다. 하지만 Java 애플리케이션도 잘못된 메모리 사용으로 인해 “새어 나가서”(OutOfMemoryError) 느려지거나 예상치 못하게 멈칫할 수 있습니다. 원인은 다양합니다:

  • 메모리 누수(memory leaks): 제거되어야 할 객체가 계속 “살아 있는” 경우.
  • 컬렉션에 데이터가 과도하게 쌓이는 경우.
  • 캐싱 로직의 오류.
  • 해제되지 않은 리소스(예: 스레드, 파일).

당신의 과제 — 이런 문제를 빠르게 찾아내는 법을 익히는 것입니다. 그렇지 않으면 프로그래머가 아니라 “버그 개발자”가 될 위험이 있습니다!

jmap이란?

jmap은 JDK에 포함된 유틸리티로, 실행 중인 Java 프로세스의 메모리 “스냅샷”(heap dump)을 얻을 수 있게 해줍니다. 이 스냅샷을 다른 도구로 열어 어떤 객체가 메모리를 차지하는지, 개수는 얼마나 되는지, 누가 참조하는지 확인할 수 있습니다.

Heap dump는 힙의 사진과 같습니다. 그 안의 모든 객체, 타입, 관계, 크기가 담겨 있습니다.

jmap 사용 방법

프로세스 PID 찾기

먼저 애플리케이션이 실행 중인 프로세스의 식별자(PID)를 알아야 합니다. 방법은 다양합니다:

jps(JDK에 포함된 또 다른 유틸리티) 사용:

jps -l

Java 프로세스 목록과 PID가 표시됩니다.

작업 관리자(Task Manager)나 Linux의 ps로도 확인할 수 있습니다.

메모리 덤프 생성

jmap -dump:format=b,file=heap.bin <PID>
  • format=b — 바이너리 형식(분석에 적합).
  • file=heap.bin — 덤프를 저장할 파일 이름.
  • <PID> — 프로세스 식별자.

예:

jmap -dump:format=b,file=heap.bin 12345

heap dump는 어떻게 분석할까?
일반적으로 그래픽 도구(예: jvisualvm 또는 Eclipse MAT)로 분석합니다. 바이너리 파일에서 수작업으로 객체를 찾는 것은 이미 고난도이자 약간 고통스러운 작업이니까요.

jmap의 기타 기능

메모리 통계 보기:

jmap -heap <PID>

힙 정보(크기, 사용하는 GC, 상태)를 보여줍니다.

클래스 목록:

jmap -histo <PID>

클래스별 통계를 보여줍니다: 타입별 객체 수와 총 크기.

출력 예:

# 객체 개수 바이트
1
[C
1200 48000
2
java.lang.String
1000 32000
3
java.util.HashMap
200 12800

이 정도면 감이 옵니다. 특정 타입의 객체가 갑자기 수백만 개라면 — 점검해 볼 이유가 있습니다.

2. jvisualvm: 시각적 메모리 분석기

jvisualvm은 JDK에 포함된 그래픽 프로그램입니다(bin 폴더를 확인하세요). 로컬(그리고 때로는 원격) Java 프로세스에 연결해 실시간 모니터링을 하고, heap dump를 찍고, 메모리·스레드·GC·CPU 통계를 확인하며, 코드 실행을 프로파일링할 수도 있습니다.

클릭하며 예쁜 그래프 보는 걸 좋아한다면 — 바로 이 도구가 제격입니다.

jvisualvm 실행 방법

명령줄(또는 Windows의 바로가기)에서:

jvisualvm

창이 열리고, 모든 로컬 Java 프로세스 목록이 보입니다.

jvisualvm의 주요 기능

실시간 메모리 모니터링

  • 왼쪽 목록에서 프로세스를 선택합니다.
  • Monitor 탭으로 이동합니다.
  • 힙 사용량, CPU, 스레드·클래스 수 그래프를 볼 수 있습니다.

예:

jvisualvm-monitor

Heap Dump (메모리 스냅샷)

  • Monitor 패널에서 Heap Dump를 클릭합니다.
  • 몇 초 후 덤프 분석 탭이 나타납니다.
  • 모든 클래스 목록, 객체 수, 총 크기를 확인할 수 있습니다.

무거운 객체 찾기

  • 크기나 개수 기준으로 정렬합니다.
  • ArrayListString 같은 타입이 과도한 메모리를 차지한다면 — 조사할 신호입니다.

참조 분석 (Reference Graph)

  • 클래스를 클릭하면 해당 클래스 객체를 누가 참조하는지 볼 수 있습니다.
  • 왜 객체가 가비지 컬렉터에 의해 제거되지 않는지 “근원”까지 추적할 수 있습니다.

스레드 분석

  • Threads 탭 — 모든 스레드와 상태를 보여줍니다.
  • “멈춘” 스레드나 과도하게 활동적인 스레드를 찾을 수 있습니다.

프로파일링

  • Profiler 탭 — 어떤 메서드가 시간 또는 메모리를 가장 많이 사용하는지 측정합니다.
  • 꽤 심층 분석이지만, 메모리 누수만 찾는다면 앞의 단계들로도 충분합니다.

3. 실습: 메모리 누수 분석

누수가 있는 코드 예시

import java.util.ArrayList;
import java.util.List;

public class MemoryLeakDemo {
    // 정적 컬렉션 — 고전적인 패턴!
    private static final List<String> bigList = new ArrayList<>();

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 1_000_000; i++) {
            bigList.add("문자열 번호 " + i);
            if (i % 100_000 == 0) {
                System.out.println("추가됨: " + i);
                Thread.sleep(500); // 분석 시간을 주자
            }
        }
        System.out.println("완료! 애플리케이션을 종료하지 말고 jvisualvm을 여세요.");
        Thread.sleep(600_000); // 10분 — 분석 시간
    }
}

무슨 일이 벌어지나요?

  • 정적 리스트를 만들고 그 안에 문자열 100만 개를 추가합니다.
  • 추가가 끝난 뒤 프로그램은 “대기”하여 분석 도구로 연결할 시간을 줍니다.

jvisualvm으로 분석하기

  1. 프로그램을 실행합니다.
  2. jvisualvm을 열고 프로세스를 선택합니다.
  3. Monitor 탭에서 메모리 그래프를 봅니다.
    • 정상이라면 GC 후 메모리가 “줄어들어야” 합니다.
    • 누수가 있으면 메모리는 계속 증가합니다.
  4. Heap Dump를 수행합니다.
  5. 어떤 객체가 메모리를 가장 많이 차지하는지 확인합니다.
    • 우리 예시에서는 java.util.ArrayListjava.lang.String입니다.
  6. 객체를 클릭해 누가 참조하는지 확인합니다.
    • 정적 필드 bigList가 보일 것입니다.

어떻게 고칠까?

  • 불필요한 요소는 컬렉션에서 제거하고, 크기 증가를 제한하세요.
  • 더 이상 필요 없는 무거운 객체에 대한 참조는 null로 설정하세요.
  • 항상 필요하지 않다면 큰 컬렉션을 static에 두지 마세요.

4. 기타 도구: Eclipse MAT, jconsole

Eclipse Memory Analyzer (MAT)

Eclipse MAT은 무료이면서도 강력한 heap dump 분석 도구입니다. 어떤 객체가 메모리를 차지하는지, 어떤 객체가 다른 객체를 붙잡고 있는지 — 이른바 retained set — 를 확인할 수 있습니다. 이를 통해 누수가 숨어 있는 지점을 정확히 파악할 수 있습니다.

의심스러운 객체에 대한 상세 보고서(leak suspects)를 생성할 수 있으며, 몇 기가바이트에 달하는 거대 덤프도 무리 없이 처리합니다.

사용법은 간단합니다. jmap 또는 jvisualvm으로 메모리 덤프를 찍고, MAT에서 열어 Leak Suspects Report 버튼을 누르면, 잠재적 문제를 이미 하이라이트한 보고서를 받을 수 있습니다.

jconsole

jconsole은 실시간으로 JVM을 관찰하기에 단순하고 편리한 도구입니다. 사용 중인 메모리, CPU 부하, 실행 중인 스레드 수, 가비지 컬렉터의 동작을 보여줍니다.

VisualVM 같은 더 진보된 도구와 달리 jconsoleheap dump를 분석하지는 않지만, 애플리케이션 내부에서 지금 무슨 일이 일어나는지 한눈에 파악해야 할 때 아주 적합합니다.

5. 메모리 분석 시 흔한 실수

오류 #1: 다른 프로세스의 덤프를 찍는다. 한 머신에서 여러 Java 애플리케이션이 돌아가는 경우가 많아 PID를 혼동하기 쉽습니다. jps로 확인하고, 여러분의 프로그램이 맞는지 꼭 검증하세요.

오류 #2: 실제로는 누수가 아닌데 “누수”를 기대한다. GC가 즉시 객체를 제거하지 않을 수 있습니다 — 때로는 메모리가 Full GC 이후에야 “풀리기도” 합니다. 한두 번의 사이클 후 메모리가 줄지 않는다고 당황하지 말고, 추세를 보세요.

오류 #3: 정적/캐시된 객체의 영향을 간과한다. 많은 누수가 static 필드나 전역 컬렉션에 객체가 보관되어 생깁니다. “무거운” 객체를 누가 참조하는지 확인하세요 — 흔히 static입니다.

오류 #4: 동적 프로파일링을 사용하지 않는다. Heap dump도 좋지만, 때로는 시간에 따른 메모리 증가( jvisualvm의 그래프 )를 보는 것이 중요합니다. 메모리가 꾸준히 증가한다면 — 조사할 이유가 있습니다.

오류 #5: 리스너/이벤트 핸들러를 제거하지 않는다. 이벤트에 구독해 놓고 해제하지 않으면 — 객체를 더 이상 사용하지 않아도 메모리에 “살아” 있게 됩니다.

팁: 도구 사용을 두려워하지 마세요! 덤프를 찍고, 그래프를 보고, 객체를 클릭해 보세요 — 그래야만 프로그램의 메모리를 “체감”하고 문제를 빠르게 찾을 수 있습니다.

1
과제
JAVA 25 SELF, 레벨 64, 레슨 3
잠금
jmap을 사용한 메모리 덤프 수집 — 디지털 스냅샷(곰) 🐻
jmap을 사용한 메모리 덤프 수집 — 디지털 스냅샷(곰) 🐻
1
과제
JAVA 25 SELF, 레벨 64, 레슨 3
잠금
jmap을 통한 클래스 통계 보기 — 디지털 동물원 인벤토리 🐒🐘🐆
jmap을 통한 클래스 통계 보기 — 디지털 동물원 인벤토리 🐒🐘🐆
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION