Java 힙 덤프 캡처 및 분석
힙 덤프에는 실행 중인 Java 애플리케이션이 Java 힙에서 사용하는 모든 라이브 개체의 스냅샷이 포함됩니다. 이 튜토리얼은 힙 덤프, 다양한 형식 및 중요성에 대해 교육합니다.
또한 OutOfMemoryError를 시연하는 예제를 통해 힙 덤프를 캡처하는 다양한 접근 방식과 이를 분석하는 도구를 안내합니다.
힙 덤프 및 형식 소개
힙에는 클래스를 인스턴스화하여 생성한 모든 개체가 포함됩니다. Java 런타임의 모든 클래스도 이 힙에서 생성됩니다.
이 힙은 JVM(Java Virtual Machine)이 시작될 때 생성되며 런타임 중에 확장/축소되어 애플리케이션에서 소멸되거나 생성된 객체를 조정할 수 있습니다.
가비지 수집 프로세스는 힙이 가득 찰 때마다 실행되며, 이 프로세스는 더 이상 사용되지 않거나 더 이상 참조되지 않는다고 말할 수 있는 모든 개체를 수집합니다(자세한 내용은 여기 메모리 관리에서 찾을 수 있습니다. 일반적으로 힙 덤프는 바이너리 형식 hprof 파일.
유형, 클래스 이름, 주소, 크기 및 인스턴스에 다른 개체에 대한 참조가 포함되어 있는지 여부와 같은 각 개체 인스턴스에 대한 자세한 정보를 검색할 수 있습니다. 힙 덤프는 다음 두 형식 중 하나일 수 있습니다.
- 휴대용 힙 덤프 형식(PHD 형식이라고도 함)
- 클래식 형식
휴대용 힙 덤프는 기본 형식이며 추가 분석을 위해 처리해야 하는 이진 형식입니다. 반면에 고전적인 형식은 사람이 읽을 수 있는 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를 일으키는 원인을 찾기 위해 힙 덤프 분석을 수행해야 합니다. 두 단계로 수행할 수 있습니다.
먼저 힙 덤프를 캡처한 다음 힙 덤프 파일을 분석하여 의심되는 이유를 찾으십시오.
힙 덤프를 캡처하는 다양한 방법
힙 덤프를 캡처하는 방법에는 여러 가지가 있습니다. 아래에서 하나씩 배워보도록 하겠습니다.
제이맵JVisualVMjcmd- 자동으로 힙 덤프 생성
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 프로세스는 로컬 아래에 나열됩니다. 원하는 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 메서드를 찾을 수 있습니다. 여기에서 outputFile 및 live 매개변수를 p0 및 p1 텍스트 필드에 지정하여 아래와 같이 dumpHeap 작업을 수행할 수 있습니다.

이제 Java 힙 덤프를 분석할 시간입니다.
Java 힙 덤프 분석
Java 힙 덤프에서는 높은 메모리를 사용하는 개체, 메모리를 해제하지 않는 개체를 찾기 위한 개체 그래프, 도달할 수 있는 개체와 연결할 수 없는 개체를 찾아야 합니다.
Eclipse Memory Analyzer (MAT)는 앞에서 생성한 Java 힙 덤프를 분석하는 데 가장 적합합니다. Memory Analyzer Tool을 시작하고 이를 위해 힙 덤프 파일을 엽니다.
MAT(Eclipse Memory Analyzer)에는 아래에 간략하게 설명된 두 가지 종류의 객체 크기가 있습니다.
얕은 힙 크기- 개체의 얕은 힙은 메모리의 크기입니다.Retained Heap Size- 개체가 가비지 수집되면 해제되는 메모리 양입니다.
힙 덤프 파일이 열리면 애플리케이션의 메모리 사용량 요약을 볼 수 있습니다. 이제 OutOfMemoryError의 원인을 쉽게 파악할 수 있습니다.
