1. 개요

jmap은 JVM을 모니터링 할 수 있는 도구이며, 주요 옵션은 다음과 같다.

  • -dump : JVM Heap 을 덤프 (binary 형식)
  • -finalizerinfo : finalize 를 기다리는 객체 정보
  • -heap : JVM Heap 정보
  • -histo : Heap histogram 정보
  • -permstat : PermGen 정보

2. 사용법

jmap을 실행하면 사용법을 보여준다.

# jmap
Usage:
    jmap [option]
        (to connect to running process)
    jmap [option]
        (to connect to a core file)
    jmap [option] [server_id@]
        (to connect to remote debug server)
 
where is one of:
                   to print same info as Solaris pmap
    -heap                to print java heap summary
    -histo[:live]        to print histogram of java object heap; if the "live"
                         suboption is specified, only count live objects
    -permstat            to print permanent generation statistics
    -finalizerinfo       to print information on objects awaiting finalization
    -dump: to dump java heap in hprof binary format
                         dump-options:
                           live         dump only live objects; if not specified,
                                        all objects in the heap are dumped.
                           format=b     binary format
                           file=  dump heap to 
                         Example: jmap -dump:live,format=b,file=heap.bin 
    -F                   force. Use with -dump:  or -histo
                         to force a heap dump or histogram when  does not
                         respond. The "live" suboption is not supported
                         in this mode.
    -h | -help           to print this help message
    -J             to pass  directly to the runtime system

3. 실행

그러면 본격적으로 Java 프로세스에 붙여보자. 대상 Java 프로세스는 Java 1.7 버전으로 기동된 Tomcat이다.

# jmap -heap 6854
Attaching to process ID 6854, please wait...
Exception in thread "main" java.lang.reflect.InvocationTargetException
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:622)
  at sun.tools.jmap.JMap.runTool(JMap.java:196)
  at sun.tools.jmap.JMap.main(JMap.java:128)
Caused by: sun.jvm.hotspot.runtime.VMVersionMismatchException: Supported versions are 23.25-b01. Target VM is 24.51-b03
  at sun.jvm.hotspot.runtime.VM.checkVMVersion(VM.java:234)
  at sun.jvm.hotspot.runtime.VM.(VM.java:297)
  at sun.jvm.hotspot.runtime.VM.initialize(VM.java:367)
  at sun.jvm.hotspot.bugspot.BugSpotAgent.setupVM(BugSpotAgent.java:598)
  at sun.jvm.hotspot.bugspot.BugSpotAgent.go(BugSpotAgent.java:493)
  at sun.jvm.hotspot.bugspot.BugSpotAgent.attach(BugSpotAgent.java:331)
  at sun.jvm.hotspot.tools.Tool.start(Tool.java:163)
  at sun.jvm.hotspot.tools.HeapSummary.main(HeapSummary.java:40)
  ... 6 more

4. 오류

그런데 위와 같이 에러가 났다.

확인 결과 이 서버에는 JDK 1.6 버전과 JDK 1.7 버전이 각각 설치되어 있었는데, 내가 사용한 jmap은 JDK 1.6 안에 있던 jmap이었고 (path에 걸려있던 jmap이 JDK 1.6 내 jmap이었음), 연결하려고 했던 Java 프로세스는 JDK 1.7로 올라온 Tomcat이었다.

OS를 설치하면서 기본적으로 OpenJDK가 설치된 가운데 Oracle (Sun) JDK를 또 설치하여 사용하는 경우에 흔히 발생할 수 있는 이슈이다.

그래서 다시 JDK 1.7 내에 포함된 jmap으로 다시 실행하였다. 그리고 아래와 같이 Heap에 대한 자세한 상황을 볼 수 있다.

# /usr/java7/bin/jmap -heap 6854
Attaching to process ID 6854, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.51-b03
 
using thread-local object allocation.
Mark Sweep Compact GC
 
Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 1073741824 (1024.0MB)
   NewSize          = 1310720 (1.25MB)
   MaxNewSize       = 17592186044415 MB
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2
   SurvivorRatio    = 8
   PermSize         = 21757952 (20.75MB)
   MaxPermSize      = 85983232 (82.0MB)
   G1HeapRegionSize = 0 (0.0MB)
 
Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 80609280 (76.875MB)
   used     = 2974568 (2.8367691040039062MB)
   free     = 77634712 (74.0382308959961MB)
   3.690106151549797% used
Eden Space:
   capacity = 71696384 (68.375MB)
   used     = 2887136 (2.753387451171875MB)
   free     = 68809248 (65.62161254882812MB)
   4.026892067527422% used
From Space:
   capacity = 8912896 (8.5MB)
   used     = 87432 (0.08338165283203125MB)
   free     = 8825464 (8.416618347167969MB)
   0.9809606215533089% used
To Space:
   capacity = 8912896 (8.5MB)
   used     = 0 (0.0MB)
   free     = 8912896 (8.5MB)
   0.0% used
tenured generation:
   capacity = 178978816 (170.6875MB)
   used     = 42964336 (40.97398376464844MB)
   free     = 136014480 (129.71351623535156MB)
   24.005263282108203% used
Perm Generation:
   capacity = 36831232 (35.125MB)
   used     = 36681104 (34.98182678222656MB)
   free     = 150128 (0.1431732177734375MB)
   99.59238941559164% used
 
16497 interned Strings occupying 2052408 bytes.

이번에는 permstat으로 확인했다. 시간이 조금 걸린다.

# /engn001/java7/bin/jmap -permstat 6854
Attaching to process ID 6854, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.51-b03
finding class loader instances ..done.
computing per loader stat ..done.
please wait.. computing liveness............................liveness analysis may be inaccurate ...
class_loader  classes  bytes  parent_loader  alive?  type
 
  2644  15545072    null    live  
0x00000000d241fe60  1  3056    null    dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d03634b8  1  3056  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d15a6f20  1  3024  0x00000000d07ab0b0  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d03638e0  1  3040    null    dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d241fee0  1  3048  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d03635a0  1  3056  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d0363860  1  3056  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d15a8080  1  3056  0x00000000d07ab0b0  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d241ff60  1  3024  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d10bd0f0  1  1880    null    dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d153fcb8  1  3048  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d1540438  1  3048  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d07ab170  1  3080  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d0363428  1  3056  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d0351010  1718  14691072  0x00000000d0362f70  live  org/apache/catalina/loader/StandardClassLoader@0x00000000fb56edb0
0x00000000d0363538  1  3064  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d03631f8  1  3024  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d153f8f8  1  3048  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d1540078  1  3048  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d0363278  1  3048    null    dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d0363720  1  3056  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d15a5440  1  3056  0x00000000d07ab0b0  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d141b170  1  3024    null    dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d241fce0  1  3048  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d03637a0  1  3056  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d03632e0  1  3056  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d0363620  1  3056  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d241fd60  1  3056    null    dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d15a5800  1  3056  0x00000000d07ab0b0  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d0363370  1  3024  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d03636a0  1  3040    null    dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d0363150  1  3056  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d241fde0  1  3056    null    dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d03638a0  1  3056  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d0362f70  101  1012744  0x00000000d0363088  live  sun/misc/Launcher$AppClassLoader@0x00000000fb00bf48
0x00000000d15a7750  1  3056  0x00000000d07ab0b0  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d241fe20  1  3056    null    dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d03634f8  1  3056  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d15a8be8  1  3056  0x00000000d07ab0b0  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d15a7390  1  3056  0x00000000d07ab0b0  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d0363820  1  3024  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d07ab0b0  1632  9616080  0x00000000d0351010  live  org/apache/catalina/loader/WebappClassLoader@0x00000000fbca2d98
0x00000000d15a8fa8  1  3056  0x00000000d07ab0b0  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d15dd018  1  3024    null    dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d241fea0  1  3048  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d03635e0  1  3056  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d15a65d0  1  3056  0x00000000d07ab0b0  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d15a5078  1  3024  0x00000000d07ab0b0  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d241ff20  1  3056    null    dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d03631b8  1  3064    null    dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d10d0e88  1  3048  0x00000000d07ab0b0  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d047ae18  0  0  0x00000000d0362f70  live  java/util/ResourceBundle$RBClassLoader@0x00000000fb0f1bc8
0x00000000d241ffa0  1  3048  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d15a7b10  1  3064  0x00000000d07ab0b0  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d21e8218  30  234776  0x00000000d07ab0b0  live  org/apache/jasper/servlet/JasperLoader@0x00000000fc9d10f8
0x00000000d0363760  1  3024    null    dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d0363238  1  3040    null    dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d15407f8  1  3048  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d03637e0  1  3024  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d0363320  2  32128    null    dead  javax/management/remote/rmi/NoCallStackClassLoader@0x00000000fb4b27b8
0x00000000d0363660  1  3056  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d241fd20  1  3056    null    dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d1a65160  1  3024  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d1340078  0  0  0x00000000d0362f70  live  java/net/URLClassLoader@0x00000000fafa00b8
0x00000000d13bb3d0  1  3024  0x00000000d07ab0b0  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d03633b0  4  13744    null    dead  javax/management/remote/rmi/NoCallStackClassLoader@0x00000000fb4b27b8
0x00000000d13cf990  1  3024  0x00000000d07ab0b0  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d1fc40a0  18  119832  0x00000000d07ab0b0  dead  java/net/URLClassLoader@0x00000000fafa00b8
0x00000000d03636e0  1  3064  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d241fda0  1  3048  0x00000000d0351010  dead  sun/reflect/DelegatingClassLoader@0x00000000fae4f888
0x00000000d0363088  14  94960    null    live  sun/misc/Launcher$ExtClassLoader@0x00000000fafa0490
 
total = 72  6224  41545144      N/A      alive=8, dead=64      N/A

5. 참고자료

좀 더 자세한 사항은 오라클 도큐먼트 http://docs.oracle.com/javase/7/docs/technotes/tools/share/jmap.html 링크를 참고하라.

* 64bit 에서는 jmap -J-d64 -heap pid 와 같이 추가 옵션을 주어야 한다고 하는데, 나는 옵션을 주지 않아도 되었다.

* 향후 버전에서는 지원되지 않을 수 있다고 한다.


6. 버그

jmap 관련 버그도 있다.

JDK-6966967 : G1: SA: jmap and jstack do not work (http://bugs.java.com/view_bug.do?bug_id=6966967)


7. 바이너리 코어

상황에 따라 jmap으로 덤프를 획득하지 못하는 경우가 있다. 이 때는 OS 바이너리 덤프로부터 힙 덤프를 추출할 수 있다. (HotSpot JVM 한정)

바이너리 덤프는 다음과 같이 생성할 수 있다. gcore에 대해서는 더 자세히 알아보자.

  • AIX : $ gencore [pid] [파일명]
  • Linux : $ gcore -o [파일명] [pid]
  • 윈도우 : 작업관리자 -> 프로세스 -> 우클릭 -> 덤프 파일 생성

바이너리 덤프가 생성되면 다음과 같은 형식으로 힙 덤프를 추출한다.

jmap -dump:format=b,file=[파일명] [자바경로] [바이너리덤프]

예는 다음과 같다.

$ jmap -dump:format=b,file=myDump.hprof /usr/bin/java core.10883