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
를 일으키는 원인을 찾기 위해 힙 덤프 분석을 수행해야 합니다. 두 단계로 수행할 수 있습니다.
먼저 힙 덤프를 캡처한 다음 힙 덤프 파일을 분석하여 의심되는 이유를 찾으십시오.
힙 덤프를 캡처하는 다양한 방법
힙 덤프를 캡처하는 방법에는 여러 가지가 있습니다. 아래에서 하나씩 배워보도록 하겠습니다.
제이맵
JVisualVM
jcmd
- 자동으로 힙 덤프 생성
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
의 원인을 쉽게 파악할 수 있습니다.