几个与JVM相关的JDK工具:jps, jstat, jmap

在项目中遇到OOM(Out of Memory)的问题,为了分析内存和JVM的垃圾回收器GC问题,一并把JVM相关的一些工具也研究了一下:

  1. jps:Java进程查看工具,实际上它和Unix/Linux上面的ps命令的功能差不多
  2. jstat:Java内存使用情况监控工具
  3. jmap:输出JVM内存中对象的工具

这些工具位于JAVA_HOME/bin目录下

?

一,jps (Java Virtual Machine Process Status Tool) JVM进程状态工具

jps用来查看host上运行的所有java进程的pid(lvmid),一般情况下使用这个工具的目的只是为了找出运行的jvm进程ID,即lvmid,然后可以进一步使用其它的工具来监控和分析JVM,因此可以说这个工具并没有太多的功能。

如我本地只启动了visualVM,jps可以看到

?View Code TEXT
1
2
2156 Jps
1932 Main

常用的几个参数:

-l 输出java应用程序的main class的完整包

-q 仅显示pid,不显示其它任何相关信息

-m 输出传递给main方法的参数

-v 输出传递给JVM的参数。在诊断JVM相关问题的时候,这个参数可以查看JVM相关参数的设置

如使用 jps -l -v 命令可以输出如下

?View Code TEXT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
C:\Oracle\Middleware\jdk160_05\bin>jps -l -v
5780 sun.tools.jps.Jps
  -Dapplication.home=C:\Oracle\Middleware\jdk160_05 -Xms8m
 
1932 org/netbeans/Main -Xms24m -Xmx192m
  -Dnetbeans.accept_license_class=com.sun.tools.visualvm.modules.startup.AcceptLicense
  -Dsun.jvmstat.perdata.syncWaitMs=10000
  -Djdk.home=C:/Oracle/Middleware/jdk160_05
  -Dnetbeans.home=D:\Arone.Zhang\jvm\visualvm_111\platform9
  -Dnetbeans.dirs=D:\Arone.Zhang\jvm\visualvm_111\visualvm;D:\Arone.Zhang\jvm\visualvm_111\profiler3
  -Dnetbeans.user=C:\Users\Arone.Zhang\AppData\Roaming\.visualvm\1.1.1
  -Dnetbeans.system_http_proxy=DIRECT
  -Dnetbeans.system_http_non_proxy_hosts=
  -Dsun.awt.keepWorkingSetOnMinimize=true

?

jps的官方描述:http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/jps.html

?

二,jstat (Java Virtual Machine Statistics Monitoring Tool) JVM统计信息监控工具

jstat根据lvmid来实时监控Java应用程序的资源和性能,包括jvm的heap size和GC的回收状况,比起jps工具jstat要强大得多,同时使用起来也较复杂。

?

jstat有如下的生成结果选项(通过 jstat –options 来查看),jstat需要列出lvmid的统计信息,因此需要和jps配合使用

-class :显示加载类和卸载类的数量、所占空间大小和加载类和卸载类所花的时间

-compiler:显示VM实时编译的统计信息,包括编译任务执行的数量、编译任务失败的数量、变为无效的编译任务数量、执行编译任务所花费的时间等等

-gc:显示GC的堆信息,包括存活区、新生代区、永久区、发生GC的次数和所花费的时间

-gccapacity:显示JVM内存池中三代(young、old、perm)对象的使用和占用能力

-gccause:这个和-gcutil选项一样都是显示GC统计信息的汇总信息,只是包括了最后GC事件和当前GC事件

-gcnew:显示新生代对象的统计信息

-gcnewcapacity:显示新生代对象的信息和占用量

-gcold:显示老一代对象的统计信息

-gcoldcapacity:显示老一代对象的信息和占用量

-gcpermcapacity:显示永久代对象的信息和占用量

-gcutil:显示GC统计信息的汇总信息

-printcompilation:打印当前VM执行的信息

?

此工具中涉及到的永久代、老一代和新生代的概念和jdk的GC算法有密切的关系,关于GC的内容请查看Tuning Garbage Collection with the 5.0 JavaTM Virtual Machine

jstat使用的除了上面生成结果选项之外,还有收集统计信息的时间间隔和统计的次数,同时还可以控制输出结果的列标题如何出现。

?View Code TEXT
1
2
3
4
5
6
7
8
C:\Oracle\Middleware\jdk160_05\bin>jstat -gcnew -t -h3 1932 30 5
Timestamp        S0C    S1C    S0U    S1U   TT MTT  DSS      EC       EU     YGC     YGCT
        26638.9  192.0  192.0    0.0  192.0  1  15   96.0   1728.0   1472.7  12737   17.803
        26639.0  192.0  192.0    0.0  192.0  1  15   96.0   1728.0   1472.7  12737   17.803
        26639.0  192.0  192.0    0.0  192.0  1  15   96.0   1728.0   1472.7  12737   17.803
Timestamp        S0C    S1C    S0U    S1U   TT MTT  DSS      EC       EU     YGC     YGCT
        26639.0  192.0  192.0    0.0  192.0  1  15   96.0   1728.0   1472.7  12737   17.803
        26639.1  192.0  192.0    0.0  192.0  1  15   96.0   1728.0   1472.7  12737   17.803

?

上面的 -t 选项是用来打印timestamp,-hN选项是用来控制打印列标题,这个选项对于多屏输出结果的时候比较有用,因此上面的jstat -gcnew -t -h3 1932 30 5 代表针对lvmid为 1932 的Java进程收集新生代对象统计信息:包括打印时间戳、每隔3行打印一次标题、30ms统计一次、共统计5次

每个选项输出列的含义请查看官方文档,jstat的官方描述:http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/jstat.html

虽然jstat提供了监控GC状态的强大功能,但是由于它是命令行,在实际问题的分析过程中难度比较高,因此市场上面出现了很多基于此命令的可视化工具,开源工具visualVM就是其中的佼佼者,JDK1.6之后,它已经包括在jdk中了。


 一些术语的中文解释:

         S0C:年轻代中第一个survivor(幸存区)的容量 (字节)
         S1C:年轻代中第二个survivor(幸存区)的容量 (字节)
         S0U:年轻代中第一个survivor(幸存区)目前已使用空间 (字节)
         S1U:年轻代中第二个survivor(幸存区)目前已使用空间 (字节)
           EC:年轻代中Eden(伊甸园)的容量 (字节)
           EU:年轻代中Eden(伊甸园)目前已使用空间 (字节)
           OC:Old代的容量 (字节)
           OU:Old代目前已使用空间 (字节)
           PC:Perm(持久代)的容量 (字节)
           PU:Perm(持久代)目前已使用空间 (字节)
         YGC:从应用程序启动到采样时年轻代中gc次数
       YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)
         FGC:从应用程序启动到采样时old代(全gc)gc次数
       FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)
         GCT:从应用程序启动到采样时gc用的总时间(s)

    NGCMN:年轻代(young)中初始化(最小)的大小 (字节)

    NGCMX:年轻代(young)的最大容量 (字节)

        NGC:年轻代(young)中当前的容量 (字节)

   OGCMN:old代中初始化(最小)的大小 (字节) 

   OGCMX:old代的最大容量 (字节)

       OGC:old代当前新生成的容量 (字节)

   PGCMN:perm代中初始化(最小)的大小 (字节) 

   PGCMX:perm代的最大容量 (字节)   

       PGC:perm代当前新生成的容量 (字节)

          S0:年轻代中第一个survivor(幸存区)已使用的占当前容量百分比

         S1:年轻代中第二个survivor(幸存区)已使用的占当前容量百分比

           E:年轻代中Eden(伊甸园)已使用的占当前容量百分比

           O:old代已使用的占当前容量百分比

           P:perm代已使用的占当前容量百分比

  S0CMX:年轻代中第一个survivor(幸存区)的最大容量 (字节)

 S1CMX :年轻代中第二个survivor(幸存区)的最大容量 (字节)

    ECMX:年轻代中Eden(伊甸园)的最大容量 (字节)

       DSS:当前需要survivor(幸存区)的容量 (字节)(Eden区已满)

          TT: 持有次数限制

       MTT : 最大持有次数限制


语法结构:

Usage: jstat -help|-options

       jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

 参数解释:

Options — 选项,我们一般使用 -gcutil 查看gc情况

vmid    — VM的进程号,即当前运行的java进程号

interval– 间隔时间,单位为秒或者毫秒

count   — 打印次数,如果缺省则打印无数次

S0  — Heap上的 Survivor space 0 区已使用空间的百分比
S1  — Heap上的 Survivor space 1 区已使用空间的百分比
E   — Heap上的 Eden space 区已使用空间的百分比
O   — Heap上的 Old space 区已使用空间的百分比
P   — Perm space 区已使用空间的百分比
YGC — 从应用程序启动到采样时发生 Young GC 的次数
YGCT– 从应用程序启动到采样时 Young GC 所用的时间(单位秒)
FGC — 从应用程序启动到采样时发生 Full GC 的次数
FGCT– 从应用程序启动到采样时 Full GC 所用的时间(单位秒)
GCT — 从应用程序启动到采样时用于垃圾回收的总时间(单位秒)

实例使用1:

[root@localhost bin]# jstat -gcutil 25444

  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT

 11.63   0.00   56.46  66.92  98.49 162    0.248    6      0.331    0.579

实例使用2:(25444是java的进程号,ps -ef | grep java)

[root@localhost bin]# jstat -gcutil 25444 1000 5

  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT

 73.54   0.00  99.04  67.52  98.49    166    0.252     6    0.331    0.583

 73.54   0.00  99.04  67.52  98.49    166    0.252     6    0.331    0.583

 73.54   0.00  99.04  67.52  98.49    166    0.252     6    0.331    0.583

 73.54   0.00  99.04  67.52  98.49    166    0.252     6    0.331    0.583

 73.54   0.00  99.04  67.52  98.49    166    0.252     6    0.331    0.583

我们可以看到,5次young gc之后,垃圾内存被从Eden space区(E)放入了Old space区(O),并引起了百分比的变化,导致Survivor space使用的百分比从73.54%(S0)降到0%(S1)。有效释放了内存空间。绿框中,我们可以看到,一次full gc之后,Old space区(O)的内存被回收,从99.05%降到67.52%。

图中同时打印了young gc和full gc的总次数、总耗时。而,每次young gc消耗的时间,可以用相间隔的两行YGCT相减得到。每次full gc消耗的时间,可以用相隔的两行FGCT相减得到。例如红框中表示的第一行、第二行之间发生了1次young gc,消耗的时间为0.252-0.252=0.0秒。

常驻内存区(P)的使用率,始终停留在98.49%左右,说明常驻内存没有突变,比较正常。

如果young gc和full gc能够正常发生,而且都能有效回收内存,常驻内存区变化不明显,则说明java内存释放情况正常,垃圾回收及时,java内存泄露的几率就会大大降低。但也不能说明一定没有内存泄露。

GCT 是YGCT 和FGCT的时间总和。

以上,介绍了Jstat按百分比查看gc情况的功能。其实,它还有功能,例如加载类信息统计功能、内存池信息统计功能等,那些是以绝对值的形式打印出来的,比较少用,在此就不做介绍。

[root@localhost bin]# ps -ef | grep java

root     25917     1  2 23:23 pts/2    00:00:05 /usr/local/jdk1.5/bin/java -Djava.endorsed.dirs=/usr/local/jakarta-tomcat-5.0.30/common/endorsed -classpath /usr/local/jdk1.5/lib/tools.jar:/usr/local/jakarta-tomcat-5.0.30/bin/bootstrap.jar:/usr/local/jakarta-tomcat-5.0.30/bin/commons-logging-api.jar -Dcatalina.base=/usr/local/jakarta-tomcat-5.0.30 -Dcatalina.home=/usr/local/jakarta-tomcat-5.0.30 -Djava.io.tmpdir=/usr/local/jakarta-tomcat-5.0.30/temp org.apache.catalina.startup.Bootstrap start

jstat -class pid:显示加载class的数量,及所占空间等信息。

实例使用3:

[root@localhost bin]# jstat -class 25917

Loaded  Bytes  Unloaded  Bytes     Time

2629    2916.8       29   24.6     0.90

jstat -compiler pid:显示VM实时编译的数量等信息。

实例使用4:

[root@localhost bin]# jstat -compiler 25917

Compiled Failed Invalid   Time   FailedType FailedMethod

     768      0       0   0.70            0

jstat –gccapacity :可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小,如:PGCMN显示的是最小perm的内存使用量,PGCMX显示的是perm的内存最大使用量,PGC是当前新生成的perm内存占用量,PC是但前perm内存占用量。其他的可以根据这个类推, OC是old内纯的占用量。

[root@localhost bin]# jstat -gccapacity 25917

NGCMN       640.0

NGCMX       4992.0

NGC         832.0

S0C         64.0

S1C         64.0

EC          704.0

OGCMN       1408.0

OGCMX       60544.0

OGC         9504.0

OC          9504.0                  OC是old内纯的占用量

PGCMN       8192.0                  PGCMN显示的是最小perm的内存使用量

PGCMX       65536.0                 PGCMX显示的是perm的内存最大使用量

PGC         12800.0                 PGC是当前新生成的perm内存占用量

PC          12800.0                 PC是但前perm内存占用量

YGC         164

FGC         6

jstat -gcnew pid: new对象的信息

[root@localhost bin]# jstat -gcnew 25917

 S0C    S1C    S0U    S1U   TT MTT  DSS      EC       EU     YGC     YGCT

 64.0   64.0   47.4   0.0   2  15   32.0    704.0    145.7    168    0.254

jstat -gcnewcapacity pid: new对象的信息及其占用量

[root@localhost bin]# jstat -gcnewcapacity 25917

 NGCMN  NGCMX   NGC   S0CMX  S0C   S1CMX  S1C   ECMX    EC      YGC   FGC

640.0  4992.0  832.0 64.0   448.0 448.0  64.0   4096.0  704.0  168     6

jstat -gcold pid: old对象的信息。

[root@localhost bin]# jstat -gcold 25917

   PC       PU        OC          OU       YGC    FGC    FGCT     GCT

 12800.0  12617.6     9504.0      6561.3   169     6    0.335    0.591

jstat -gcoldcapacity pid:old对象的信息及其占用量。

[root@localhost bin]# jstat -gcoldcapacity 25917

OGCMN      OGCMX        OGC         OC       YGC   FGC    FGCT     GCT

1408.0     60544.0      9504.0      9504.0   169     6    0.335    0.591

jstat -gcpermcapacity pid: perm对象的信息及其占用量。

[root@localhost bin]# jstat -gcpermcapacity 25917

PGCMN      PGCMX       PGC         PC      YGC   FGC    FGCT     GCT

8192.0    65536.0    12800.0    12800.0   169     6    0.335    0.591

jstat -printcompilation pid:当前VM执行的信息。

[root@localhost bin]# jstat -printcompilation -h3  25917 1000 5

每1000毫秒打印一次,一共打印5次,还可以加上-h3每三行显示一下标题。

Compiled  Size  Type Method

     788     73    1 java/io/File <init>

     788     73    1 java/io/File <init>

     788     73    1 java/io/File <init>

Compiled  Size  Type Method

     788     73    1 java/io/File <init>

     788     73    1 java/io/File <init>

三,jmap (Memory Map) JVM内存对象打印工具

jmap根据lvmid来Java进程的内存所有对象

?

首先根据选项打印出Java对象堆的直方图信息,包括Java类、类实例对象数量、在内存中占有量的大小以及类的完整包名,下面是缩减版本的直方图信息:

?View Code TEXT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
C:\Oracle\Middleware\jdk160_05\bin&gt;jmap -histo 3432
 
 num     #instances         #bytes  class name
----------------------------------------------
   1:         14644       20883096  [I
   2:         69176        7689640  
   3:        111994        6561320  [C
   4:         69176        5542192  
   5:          1933        4825592  [Ljavax.swing.text.GapContent$MarkData;
   6:        100440        4480456  
   7:          6506        3771008  
   8:         90461        3618440  org.openide.util.RequestProcessor$Item
   9:         90239        3609560  org.openide.util.RequestProcessor$EnqueueTask
......
3315:             1              8  sun.misc.Launcher$Factory
3316:             1              8  org.netbeans.core.windows.WindowSystemImpl
3317:             1              8  javax.swing.filechooser.WindowsFileSystemView
3318:             1              8  org.netbeans.ModuleFactory
Total       1767075      103137272


 jmap -heap pid

         查看java 堆(heap)使用情况

 

        using thread-local object allocation.
        Parallel GC with 4 thread(s)          //GC 方式

         Heap Configuration:       //堆内存初始化配置
         MinHeapFreeRatio=40     //对应jvm启动参数-XX:MinHeapFreeRatio设置JVM堆最小空闲比率(default 40)
         MaxHeapFreeRatio=70  //对应jvm启动参数 -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率(default 70)
         MaxHeapSize=512.0MB  //对应jvm启动参数-XX:MaxHeapSize=设置JVM堆的最大大小
         NewSize  = 1.0MB          //对应jvm启动参数-XX:NewSize=设置JVM堆的‘新生代’的默认大小
         MaxNewSize =4095MB   //对应jvm启动参数-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
         OldSize  = 4.0MB            //对应jvm启动参数-XX:OldSize=<value>:设置JVM堆的‘老生代’的大小
         NewRatio  = 8         //对应jvm启动参数-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
         SurvivorRatio = 8    //对应jvm启动参数-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值 
          PermSize= 16.0MB       //对应jvm启动参数-XX:PermSize=<value>:设置JVM堆的‘永生代’的初始大小
          MaxPermSize=64.0MB  //对应jvm启动参数-XX:MaxPermSize=<value>:设置JVM堆的‘永生代’的最大大小

 

 

          Heap Usage:               //堆内存分步
          PS Young Generation
          Eden Space:          //Eden区内存分布
            capacity = 20381696 (19.4375MB)  //Eden区总容量
            used     = 20370032 (19.426376342773438MB)  //Eden区已使用
            free     = 11664 (0.0111236572265625MB)  //Eden区剩余容量
            99.94277218147106% used  //Eden区使用比率
         From Space:        //其中一个Survivor区的内存分布
             capacity = 8519680 (8.125MB)
             used     = 32768 (0.03125MB)
             free     = 8486912 (8.09375MB)
             0.38461538461538464% used
        To Space:            //另一个Survivor区的内存分布
            capacity = 9306112 (8.875MB)
            used     = 0 (0.0MB)
            free     = 9306112 (8.875MB)
            0.0% used
        PS Old Generation  //当前的Old区内存分布
            capacity = 366280704 (349.3125MB)
            used     = 322179848 (307.25464630126953MB)
            free     = 44100856 (42.05785369873047MB)
            87.95982001825573% used
        PS Perm Generation  //当前的 “永生代” 内存分布
            capacity = 32243712 (30.75MB)
            used     = 28918584 (27.57891082763672MB)
            free     = 3325128 (3.1710891723632812MB)
            89.68751488662348% used

 

    3. jmap - dump  pid

          将内存使用的详细情况输出到文件

          map -dump:format=b,file= m.dat pid

           用jhat命令可以参看 jhat -port 5000 m.dat 
        在浏览器中访问:http://localhost:5000/  查看详细信息


其次是将内存堆的信息dump成为一个二进制文件,供jhat这样的Java内存分析工具来使用,如下降Java内存对象堆dump为heap.bin文件:

?View Code TEXT
1
2
3
C:\Oracle\Middleware\jdk160_05\bin&gt;jmap -dump:format=b,file=heap.bin 3432
Dumping heap to C:\Oracle\Middleware\jdk160_05\bin\heap.bin ...
Heap dump file created

命令运行完成之后,在同目录下会生成一个heap.bin二进制内存对象堆文件

?

此工具使用比较简单,但是它对于分析内存溢出问题非常有用,目前有很多用来分析Java内存对象的工具,如收费的工具有:jprofiler,YourKit Java Profiler;而像Eclipse MAT则是优秀的内存对象分析开源工具,后续将整理相关的文章。



另附:jvm启动参数

一:JVM启动参数共分为三类: 
        其一是标准参数(-),所有的JVM实现都必须实现这些参数的功能,而且向后兼容; 
        其二是非标准参数(-X),指的是JVM底层的一些配置参数,这些参数在一般开发中默认即可,不需要任何配置。但是在生产环境中,并不保证所有jvm实现 都满足,所以为了提高性能,往往需要调整这些参数,以求系统达到最佳性能。另外这些参数不保证向后兼容,也即是说“如有变更,恕不在后续版本的JDK通 知”(这是官网上的原话); 
        其三是非Stable参数(-XX),这类参数在jvm中是不稳定的,不适合日常使用的,后续也是可能会在没有通知的情况下就直接取消了,需要慎重使用。 
二:而JVM内存又可分为三个主要的域: 
        新域、旧域以及永久域。JVM生成的所有新对象放在新域中。一旦对象经历了一定数量的垃圾收集循环后,便进入旧域。而在永久域中是用来存储JVM自己的反 射对象的,如class和method对象,而且GC(Garbage Collection)不会在主程序运行期对永久域进行清理。其中新域和旧域属于堆,永久域是一个独立域并且不认为是堆的一部分。 
三:各主要参数的作用如下: 
        -Xms:设置jvm内存的初始大小 
        -Xmx:设置jvm内存的最大值 
        -Xmn:设置新域的大小(这个似乎只对 jdk1.4来说是有效的,后来就废弃了) 
        -Xss:设置每个线程的堆栈大小(也就是说,在相同物理内存下,减小这个值能生成更多的线程) 
        -XX:NewRatio :设置新域与旧域之比,如-XX:NewRatio = 4就表示新域与旧域之比为1:4 
        -XX:NewSize:设置新域的初始值 
        -XX:MaxNewSize :设置新域的最大值 
        -XX:MaxPermSize:设置永久域的最大值 
        -XX:SurvivorRatio=n:设置新域中Eden区与两个Survivor区的比值。(Eden区主要是用来存放新生的对象,而两个Survivor区则用来存放每次垃圾回收后存活下来的对象) 


阅读更多
个人分类: 深入java 云计算
想对作者说点什么? 我来说一句

JDK自带VM分析工具

2012年05月29日 4KB 下载

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭