본문 바로가기
JAVA

메모리 덤프(dump) 분석 [ jps, jmap, jhat ]

by 윾수 2022. 5. 16.

JAVA 어플리케이션을 구동할때 메모리 사용량은 중요하다 static 메모리를 필요 이상으로 많이 사용하거나  힙메모리의 사용량이 이상하게 점점증가해서 메모리 초과가 발생하는 등 메모리 사용량의 이상 동작이 느껴질때는 메모리 덤프를 떠서 직접 확인해보는게 가장 좋다.

 

물론 더 유용한 도구들도 많겠지만 java-jdk가 깔려있다면 누구나 설치되어있는 가장 기본적인 툴을 사용 할 줄 알아야 추후 어떤 환경에서도 덤프 기능을 원활히 사용 할 수 있을것 같아 jps, jmap, jhat을 사용하려 한다.

jdk 위치에 기본적으로 사용 할 수 있는 jhat, jmap, jps가 내장 되어있음을 확인 할 수 있다.


1. jps 명령어

유닉스의 명령어중 'ps'는 현재 실행되고 있는 프로세스들을 표시하는 명령어인데

'jps'명령어는 현재 실행되고있는 JVM 프로세스를 표시해준다.

kys@DESKTOP-7JHK5HR  ~  jps
4176 Bootstrap
6081 Jps
5208 MonitoringServer
4543 SocketServer

참고. jps -v 옵션을 사용하면 구동시 입력했던 JVM 파라미터도 함께 보여준다.

 

2. jmap 명령어

jmap명령어는 jvm의 맵을 보여주는 기능을 제공하는 기본 분석 도구이다.

따라서 자바 힙 메모리의 정보를 얻거나 메모리 dump를 떠서 분석을 할 수 있다.

 kys@DESKTOP-7JHK5HR  ~  jmap -heap 4543
Attaching to process ID 4543, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.312-b07

using parallel threads in the new generation.
using thread-local object allocation.
Concurrent Mark-Sweep GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 2147483648 (2048.0MB)
   NewSize                  = 697892864 (665.5625MB)
   MaxNewSize               = 697892864 (665.5625MB)
   OldSize                  = 375848960 (358.4375MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
Exception in thread "main" java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at sun.tools.jmap.JMap.runTool(JMap.java:201)
        at sun.tools.jmap.JMap.main(JMap.java:130)
Caused by: java.lang.RuntimeException: unknown CollectedHeap type : class sun.jvm.hotspot.gc_interface.CollectedHeap
        at sun.jvm.hotspot.tools.HeapSummary.run(HeapSummary.java:144)
        at sun.jvm.hotspot.tools.Tool.startInternal(Tool.java:260)
        at sun.jvm.hotspot.tools.Tool.start(Tool.java:223)
        at sun.jvm.hotspot.tools.Tool.execute(Tool.java:118)
        at sun.jvm.hotspot.tools.HeapSummary.main(HeapSummary.java:49)
        ... 6 more

잘 나가다가 위와 같은 오류가 발생했다. 

oracle jdk같은 경우 문제가 발생 하지 않지만 openjdk같은 경우 위와 같은 문제가 발생한다고 한다.

따라서 해결하려면 debug기능을 포함한 jdk패키지를 받으면 된다.

openjdk-8 버전을 사용중이라서 해당 JVM에 맞는 debug패키지를 설치해준다.

 kys@DESKTOP-7JHK5HR  ~  sudo apt list | grep openjdk-8

WARNING: apt does not have a stable CLI interface. Use with caution in scripts.


openjdk-8-dbg/focal-updates,focal-security 8u312-b07-0ubuntu1~20.04 amd64
openjdk-8-demo/focal-updates,focal-security 8u312-b07-0ubuntu1~20.04 amd64
openjdk-8-doc/focal-updates,focal-security 8u312-b07-0ubuntu1~20.04 all
openjdk-8-jdk-headless/focal-updates,focal-security,now 8u312-b07-0ubuntu1~20.04 amd64 [installed,automatic]
openjdk-8-jdk/focal-updates,focal-security,now 8u312-b07-0ubuntu1~20.04 amd64 [installed]
openjdk-8-jre-headless/focal-updates,focal-security,now 8u312-b07-0ubuntu1~20.04 amd64 [installed,automatic]
openjdk-8-jre-zero/focal-updates,focal-security 8u312-b07-0ubuntu1~20.04 amd64
openjdk-8-jre/focal-updates,focal-security,now 8u312-b07-0ubuntu1~20.04 amd64 [installed,automatic]
openjdk-8-source/focal-updates,focal-security 8u312-b07-0ubuntu1~20.04 all

 kys@DESKTOP-7JHK5HR  ~  sudo apt install openjdk-8-dbg
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
  openjdk-8-dbg
0 upgraded, 1 newly installed, 0 to remove and 163 not upgraded.
Need to get 2050 kB of archives.
After this operation, 6538 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu focal-updates/universe amd64 openjdk-8-dbg amd64 8u312-b07-0ubuntu1~20.04 [2050 kB]
Fetched 2050 kB in 4s (536 kB/s)
Selecting previously unselected package openjdk-8-dbg:amd64.
(Reading database ... 49108 files and directories currently installed.)
Preparing to unpack .../openjdk-8-dbg_8u312-b07-0ubuntu1~20.04_amd64.deb ...
Unpacking openjdk-8-dbg:amd64 (8u312-b07-0ubuntu1~20.04) ...
Setting up openjdk-8-dbg:amd64 (8u312-b07-0ubuntu1~20.04) ...
 kys@DESKTOP-7JHK5HR  ~  jmap -heap 4543
Attaching to process ID 4543, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.312-b07

using parallel threads in the new generation.
using thread-local object allocation.
Concurrent Mark-Sweep GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 2147483648 (2048.0MB)
   NewSize                  = 697892864 (665.5625MB)
   MaxNewSize               = 697892864 (665.5625MB)
   OldSize                  = 375848960 (358.4375MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 628162560 (599.0625MB)
   used     = 545135192 (519.8814315795898MB)
   free     = 83027368 (79.18106842041016MB)
   86.78250292408386% used
Eden Space:
   capacity = 558432256 (532.5625MB)
   used     = 529401240 (504.8763656616211MB)
   free     = 29031016 (27.686134338378906MB)
   94.8013361176615% used
From Space:
   capacity = 69730304 (66.5MB)
   used     = 15733952 (15.00506591796875MB)
   free     = 53996352 (51.49493408203125MB)
   22.564008899201127% used
To Space:
   capacity = 69730304 (66.5MB)
   used     = 0 (0.0MB)
   free     = 69730304 (66.5MB)
   0.0% used
concurrent mark-sweep generation:
   capacity = 375848960 (358.4375MB)
   used     = 0 (0.0MB)
   free     = 375848960 (358.4375MB)
   0.0% used

위와같이 정상적으로 해당 java process에 대한 heap메모리 사용현황 요약정보를 얻을 수 있다.

 

두번째.

-histo옵션을 이용하여 JVM 프로세스의 메모리 통계[histogram]를 알아 볼 수 있다.

 kys@DESKTOP-7JHK5HR  ~  jmap -histo 4543

 num     #instances         #bytes  class name
----------------------------------------------
   1:        192270      326561288  [B
   2:        507238       77324520  [C
   3:        164075       68671040  [I
   4:        578939       18526048  java.lang.StackTraceElement
   5:        249436        9977440  java.util.TreeMap$Entry
   6:        131881        9069976  [Ljava.lang.Object;
   7:        291253        6990072  java.lang.String
   8:         78736        6215256  [S
   9:         46477        5986992  [Ljava.lang.StackTraceElement;
  10:         55433        2732384  [Ljava.lang.String;
  11:        141343        2261488  java.lang.Integer
  12:         29570        1892480  java.nio.DirectByteBuffer
  13:         71938        1726512  java.lang.StringBuilder
  14:         41156        1646240  java.util.PriorityQueue$Itr
  15:         31379        1506192  java.util.HashMap
  16:         15490        1115280  ch.qos.logback.classic.spi.LoggingEvent
  .
  .
  .
  .
  .
  .
  Total       3362721      574166208

위와같이 히스토그램을 보고 특정 클래스가 유별나게 많이 할당되어있다면 해당 소스를 확인 해 볼 수 있다.

 

마지막.

메모리덤프 파일을 생성하여 다른 분석 도구를 이용하여 확인 하는 방식이다.

## 명령어
$) jmap -dump:file={dump file 이름} PID

보통 덤프파일은 .hprof 확장자를 사용한다.

test.hprof를 생성해보자

 kys@DESKTOP-7JHK5HR  ~  jmap -dump:file=test.hprof 4543
Dumping heap to /home/kys/test.hprof ...
Heap dump file created

 

3. jhat (Java Heap Anlyzer Tool)

jmap으로 만들어놓은 .hprof 힙 메모리 덤프파일을 분석하는 가장 기본적인 분석 도구로서

jdk에 내장되어있다.

## 명령어
jhat {dump file}

위의 명령어를 사용하면 7000번 포트를 사용하는 웹서버로 배포가 진행된다.

contextPath  = / (루트) 이며 사용자가 7000번을 사용하고있다면 -port 옵션으로 포트를 변경하여 배포 할 수 있다.

 

 kys@DESKTOP-7JHK5HR  ~  jhat test.hprof
Reading from test.hprof...
Dump file created Mon May 16 11:55:14 KST 2022
Snapshot read, resolving...
Resolving 488975 objects...
Chasing references, expect 97 dots.................................................................................................
Eliminating duplicate references.................................................................................................
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

해당 화면이 뜨면서 커맨드를 더이상 입력 할 수 없는 상태로 되며 배포가 진행되었음을 확인 할 수있다.

(만약 커맨드를 계속 치고싶다면 nohup 명령어를 이용해서 실행하면 되겠죠?)

위와같은 화면이 뜨면서 클릭하면서 하나씩 확인 할 수 있다.

4. 기타 분석 도구들

jmap으로 hprof파일을 생성한뒤 다른 분석도구를 이용하여 해당 덤프내용을 분석 할 수도 있다.

Eclipse Memory Analyzer 라는 분석도구를 많이 사용 하는것 같다.

Eclipse Memory Analyzer

내가 사용하고 있는 IntelliJ 에서도 파일을 열어보니 제대로 작동하는것 같다.

나중에 써보고 어떤게 좋은지 알아보면 될것같다.

 

인텔리제이 에서도 그래로 보는거 있음...

'JAVA' 카테고리의 다른 글

Scanner 클래스 사용법  (0) 2021.09.14