Java 힙 덤프 캡처 및 분석

Mehvish Ashiq 2024년2월15일
  1. 힙 덤프 및 형식 소개
  2. Java에서 OutOfMemoryError를 유발하는 예제 코드
  3. 힙 덤프를 캡처하는 다양한 방법
  4. Java 힙 덤프 분석
Java 힙 덤프 캡처 및 분석

힙 덤프에는 실행 중인 Java 애플리케이션이 Java 힙에서 사용하는 모든 라이브 개체의 스냅샷이 포함됩니다. 이 튜토리얼은 힙 덤프, 다양한 형식 및 중요성에 대해 교육합니다.

또한 OutOfMemoryError를 시연하는 예제를 통해 힙 덤프를 캡처하는 다양한 접근 방식과 이를 분석하는 도구를 안내합니다.

힙 덤프 및 형식 소개

힙에는 클래스를 인스턴스화하여 생성한 모든 개체가 포함됩니다. Java 런타임의 모든 클래스도 이 힙에서 생성됩니다.

이 힙은 JVM(Java Virtual Machine)이 시작될 때 생성되며 런타임 중에 확장/축소되어 애플리케이션에서 소멸되거나 생성된 객체를 조정할 수 있습니다.

가비지 수집 프로세스는 힙이 가득 찰 때마다 실행되며, 이 프로세스는 더 이상 사용되지 않거나 더 이상 참조되지 않는다고 말할 수 있는 모든 개체를 수집합니다(자세한 내용은 여기 메모리 관리에서 찾을 수 있습니다. 일반적으로 힙 덤프는 바이너리 형식 hprof 파일.

유형, 클래스 이름, 주소, 크기 및 인스턴스에 다른 개체에 대한 참조가 포함되어 있는지 여부와 같은 각 개체 인스턴스에 대한 자세한 정보를 검색할 수 있습니다. 힙 덤프는 다음 두 형식 중 하나일 수 있습니다.

  1. 휴대용 힙 덤프 형식(PHD 형식이라고도 함)
  2. 클래식 형식

휴대용 힙 덤프는 기본 형식이며 추가 분석을 위해 처리해야 하는 이진 형식입니다. 반면에 고전적인 형식은 사람이 읽을 수 있는 ASCII 텍스트입니다.

이 두 가지 형식에 대해 여기에서 읽을 수 있습니다.

힙 덤프 사용의 중요성

일반적으로 OutOfMemoryError 또는 예상보다 많은 메모리를 소비하는 Java 애플리케이션으로 인해 애플리케이션이 충돌하는 경우 힙 덤프의 이점을 얻습니다.

힙 덤프는 오류의 주요 원인 및 기타 세부 정보(예: 모든 클래스의 개체 수, 모든 클래스의 메모리 사용량 등)를 식별하는 데 도움이 됩니다.

또한 애플리케이션의 각 Java 개체가 차지하는 메모리 크기를 캡처하는 데 도움이 됩니다. 캡처된 이 모든 정보는 메모리 누수 문제를 일으키는 실제 코드를 찾는 데 유용할 수 있습니다.

Java 힙 덤프를 캡처하는 다양한 방법으로 이어지는 OutOfMemoryError를 유발하는 코드 예제를 살펴보겠습니다.

Java에서 OutOfMemoryError를 유발하는 예제 코드

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

public class Test {
  public static void main(String[] args) {
    List<byte[]> myList = new ArrayList<>();
    int index = 0;

    while (true) {
      // 1MB each iteration, 1 x 1024 x 1024 = 1048576
      byte[] bytes = new byte[1048576];
      myList.add(bytes);
      Runtime runTime = Runtime.getRuntime();
      System.out.printf("[%d] free memory is: %s%n", index++, runTime.freeMemory());
    }
  }
}

위의 코드는 Java Virtual Machine이 충분한 메모리를 할당할 수 없는 특정 지점에 도달할 때까지 while 루프를 실행하여 메모리 할당을 계속합니다.

이 시점에서 java.lang.OutOfMemoryError: Java 힙 공간 오류가 발생합니다.

...
...
...
[1510] free memory is: 14687728
[1511] free memory is: 12590576
[1512] free memory is: 10493424
[1513] free memory is: 8396272
[1514] free memory is: 6299120
[1515] free memory is: 4201968
[1516] free memory is: 2104320
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at Test.main(Test.java:16)

여기에서 OutOfMemoryError를 일으키는 원인을 찾기 위해 힙 덤프 분석을 수행해야 합니다. 두 단계로 수행할 수 있습니다.

먼저 힙 덤프를 캡처한 다음 힙 덤프 파일을 분석하여 의심되는 이유를 찾으십시오.

힙 덤프를 캡처하는 다양한 방법

힙 덤프를 캡처하는 방법에는 여러 가지가 있습니다. 아래에서 하나씩 배워보도록 하겠습니다.

  1. 제이맵
  2. JVisualVM
  3. jcmd
  4. 자동으로 힙 덤프 생성
  5. JMX

jmap을 사용하여 힙 덤프 캡처

jmap 도구는 실행 중인 JVM(Java Virtual Machine)의 메모리 통계를 인쇄합니다. 원격 및 로컬 프로세스에도 사용할 수 있습니다.

-dump 옵션을 사용하여 JDK 홈 디렉토리의 bin 폴더에서 사용할 수 있는 jmap 도구를 사용하여 힙 덤프를 캡처합니다.

통사론:

jmap -dump:[live],format=b,file=<file-path> <pid>

예:

C:\Program Files\Java\jdk-18\bin> jmap -dump:live,format=b,file=/temp/dump.hprof 12876

다음은 위에서 지정한 옵션에 대한 간략한 설명입니다.

모수 설명
live 이 매개변수는 선택사항입니다. 설정되어 있으면 활성 참조가 있는 개체만 인쇄합니다. 가비지 수집 준비가 된 항목은 생략합니다.
format=b 덤프 파일이 저장될 형식을 지정하는 데 사용됩니다. 여기서는 b를 사용했는데, 이는 이 덤프 파일이 이진 형식임을 의미합니다. 이 매개변수를 설정하지 않으면 결과는 동일합니다.
file 덤프가 기록될 파일입니다.
pid Java 프로세스의 ID를 나타냅니다.

jps 명령을 사용하여 pid를 얻을 수 있습니다. 또한 jmap은 JDK의 실험적 도구로 도입되었으며 지원되지 않습니다. 따라서 경우에 따라 jmap을 사용하는 대신 다른 도구를 사용해야 할 수도 있습니다.

JVisualVM을 사용하여 힙 덤프 캡처

JVisualVM은 Java 애플리케이션 프로파일링 및 문제 해결을 추적할 수 있는 그래픽 사용자 인터페이스입니다. 간단하고 사용하기 쉬우며 힙 덤프를 캡처할 수 있습니다.

this에 따르면 Oracle JDK 6, 7 및 8에서 사용할 수 있었습니다. JDK 9 이상부터 JVisualVM은 더 이상 Oracle JDK와 함께 제공되지 않습니다. 사용자가 사용하려면 별도로 다운로드해야 합니다.

visualvm.github.io에서 다운로드하고, .zip 파일을 추출하고, bin 폴더에서 visualvm.exe를 찾아 더블 클릭하고, 프롬프트가 표시되면 동의함 버튼을 누르고, 다음 화면이 표시됩니다.

java 힙 덤프 캡처 및 분석 - visualvm 시작 창

현재 실행 중인 모든 Java 프로세스는 로컬 아래에 나열됩니다. 원하는 Java 프로세스를 선택하고 마우스 오른쪽 버튼을 클릭한 다음 Heap Dump 옵션을 선택하여 힙 덤프를 캡처할 수 있습니다.

필요한 모든 정보를 보여주는 새 탭이 열립니다.

jcmd를 사용하여 힙 덤프 캡처

이 도구는 JDK 홈 디렉토리의 bin 폴더에서도 찾을 수 있습니다. 이 경우 C:\Program Files\Java\jdk-18\bin입니다. 기본 위치에 Java를 설치하지 않은 경우에는 다를 수 있습니다.

jcmd는 Java Virtual Machine에 명령 요청을 보냅니다. Java 프로세스를 실행하는 동일한 시스템에서 사용해야 한다는 점을 기억하십시오.

여기에는 여러 명령이 있으며 그 중 하나는 GC.heap-dump이며 프로세스 ID(pid)와 출력 파일의 경로를 지정하여 힙 덤프를 캡처하는 데 사용할 것입니다. 아래 jcmd 명령의 구문을 참조하십시오.

통사론:

jcmd <pid> GC.head_dump <file-path>

예:

C:\Program Files\Java\jdk-18\bin> jcmd 12876 GC.head_dump /temp/dump.hprof

jmap과 마찬가지로 바이너리 형식으로 덤프도 생성합니다.

자동으로 힙 덤프 캡처

우리가 배운 모든 접근 방식은 특정 시간에 수동으로 힙 덤프를 캡처하지만 일부 상황에서는 java.lang.OutOfMemoryError가 발생하는 즉시 힙 덤프를 생성해야 합니다.

여기에서 자동으로 힙 덤프를 생성하면 오류를 조사하는 데 도움이 됩니다.

이러한 시나리오를 고려할 때 Java는 애플리케이션이 java.lang.OutOfMemoryError를 발생시킬 때 힙 덤프를 생성할 수 있는 명령줄 옵션인 HeapDumpOnOutOfMemoryError 와 함께 제공됩니다.

java -XX:+HeapDumpOnOutOfMemoryError

기본적으로 위의 명령은 애플리케이션이 실행 중인 java_pid<pid>.hprof 파일에 덤프를 저장합니다. 사용자 지정 디렉터리 또는 파일을 지정하고 HeapDumpPath 옵션에서 설정할 수 있습니다.

통사론:

java -XX:+HeapDumpOnOutOfMemoryError  -XX:HeapDumpPath=<file-or-dir-path>

예:

java -XX:+HeapDumpOnOutOfMemoryError  -XX:HeapDumpPath=/temp/heapdump.bin

이제 이 옵션을 통해 애플리케이션의 메모리가 부족할 때마다 다음과 같이 로그에서 힙 덤프가 포함된 생성된 파일을 찾을 수 있습니다.

java.lang.OutOfMemoryError: Requested array size exceeds VM limit
During heap to java_pid12876.hprof...
  Exception in thread "main" Head dump file created [4745371 bytes in 0.028 secs]
  java.lang.OutOfMemoryError: Requested array size exceeds VM limit

위의 텍스트가 java_pid12876.hprof 파일에 작성되고 이 옵션을 사용하여 애플리케이션을 실행하는 동안 오버헤드가 없음을 알 수 있습니다.

OutOfMemoryError가 언제 발생할지 알 수 없기 때문에 모든 애플리케이션, 특히 프로덕션에서 이 옵션을 사용하는 것이 좋습니다.

HotSpotDiagnostic MBean을 사용하여 런타임에 이 옵션을 사용할 수 있음을 기억하십시오. 이를 위해 JConsole이 사용되고 HeapDumpOnOutOfMemoryError VM 옵션을 true로 설정합니다.

JMX를 사용하여 힙 덤프 캡처

이 방법에서는 다음 두 매개변수를 허용하는 dumpHeap 방법을 제공하는 HotSpotDiagnostic MBean을 사용합니다.

모수 설명
outputFile 덤프용 출력 파일의 경로입니다. 이 파일에는 덤프를 보관할 .hprof 확장자가 있어야 합니다.
live true로 설정하면 이 튜토리얼에서 jmap을 사용하면서 배운 것처럼 메모리의 활성 객체만 덤프합니다.

힙 덤프를 캡처하거나 프로그래밍 방식으로 호출하거나 JDK 홈 디렉토리의 bin 폴더에 있는 JConsole과 같은 JMX 클라이언트를 사용하는 두 가지 방법으로 호출할 수 있습니다. 여기서는 JMX를 사용하지만 여기에서 프로그래밍 방식으로 JMX를 호출하는 방법을 배울 수 있습니다.

JMX 클라이언트(JConsole)를 통해 HotSpotDiagnostic MBean을 사용하는 것이 JConsole을 열고 실행 중인 Java 프로세스에 연결하고 MBeans 탭으로 이동한 다음 com에서 HotSpotDiagnostic을 찾는 가장 쉬운 방법입니다. .sun.management.

Operations 드롭다운에서 dumpHeap 메서드를 찾을 수 있습니다. 여기에서 outputFilelive 매개변수를 p0p1 텍스트 필드에 지정하여 아래와 같이 dumpHeap 작업을 수행할 수 있습니다.

java 힙 덤프 캡처 및 분석 - mbeans

이제 Java 힙 덤프를 분석할 시간입니다.

Java 힙 덤프 분석

Java 힙 덤프에서는 높은 메모리를 사용하는 개체, 메모리를 해제하지 않는 개체를 찾기 위한 개체 그래프, 도달할 수 있는 개체와 연결할 수 없는 개체를 찾아야 합니다.

Eclipse Memory Analyzer (MAT)는 앞에서 생성한 Java 힙 덤프를 분석하는 데 가장 적합합니다. Memory Analyzer Tool을 시작하고 이를 위해 힙 덤프 파일을 엽니다.

MAT(Eclipse Memory Analyzer)에는 아래에 간략하게 설명된 두 가지 종류의 객체 크기가 있습니다.

  1. 얕은 힙 크기 - 개체의 얕은 힙은 메모리의 크기입니다.
  2. Retained Heap Size - 개체가 가비지 수집되면 해제되는 메모리 양입니다.

힙 덤프 파일이 열리면 애플리케이션의 메모리 사용량 요약을 볼 수 있습니다. 이제 OutOfMemoryError의 원인을 쉽게 파악할 수 있습니다.

Mehvish Ashiq avatar Mehvish Ashiq avatar

Mehvish Ashiq is a former Java Programmer and a Data Science enthusiast who leverages her expertise to help others to learn and grow by creating interesting, useful, and reader-friendly content in Computer Programming, Data Science, and Technology.

LinkedIn GitHub Facebook

관련 문장 - Java Heap