jvm调优命令

  运用jvm自带的命令可以方便的在生产监控和打印堆栈的日志信息帮忙我们来定位问题!虽然jvm调优成熟的工具已经有很多:jconsole、大名鼎鼎的VisualVM,IBM的Memory Analyzer等等,但是在生产环境出现问题的时候,一方面工具的使用会有所限制,另一方面喜欢装X的我们,总喜欢在出现问题的时候在终端输入一些命令来解决。所有的工具几乎都是依赖于jdk的接口和底层的这些命令,研究这些命令的使用也让我们更能了解jvm构成和特性。

Sun JDK监控和故障处理命令有jps jstat jmap jhat jstack jinfo下面做一一介绍

 jps(虚拟机进程状况工具)

JVM Preocess Status Tool,显示正在运行的虚拟机进程。他的功能和ps命令类似。

 jps [option] [hostid]

 其中hostid默认为本机,而option选项包含以下选项

  • -l : 输出主类全名或jar路径

  • -q : 只输出LVMID

  • -m : 输出JVM启动时传递给main()的参数

  • -v : 输出JVM启动时显示指定的JVM参数

示例:

jps -q :只显示pid,不显示class名称,jar文件名和传递给main 方法的参数。

  

jps -l :输出应用程序main class的完整package名或者应用程序的jar文件完整路径名

  

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

  

jps -v:输出传递给JVM的参数

jstat(虚拟机统计信息监控工具)

JVM Statistics Monitoring Tool是用于监控虚拟机各个运行状态信息的命令工具。他可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。

 格式如下:

jstat[option vmid[interval[s|ms][count]]]

    -gc       监视Java堆状况,包括Eden区、两个survivor区、老年代、永久代等的容量、已用空间、GC时间合计信息                                                        
    -class        监视类装载、卸载数量、总空间以及类装载所耗时间
    -gcnew     监视新生代GC情况
    -gcold       监视老年代GC情况
    -compiler     输出JIT编译器编译过的方法、耗时信息
    -printcompilation 输出已经被JIT编译的方法

示例:

-gc

如上图,其中17953代表pid,200代表每隔200毫秒查询一次,10代表一共查询10次。

参数解析:

    -S0C       存活区0的总容量
    -S1C       存活区1的总容量
    -SOU      存活区0已使用的容量
    -S1U       存活区1已使用的容量
    -EC         Eden区的总容量(新生代)
    -EU         Eden区使用的容量
    -OC        Old区的总容量
    -OU        Old区已使用的容量
              -YGC      新生代垃圾回收次数
              -YGCT    新生代垃圾回收时间
              -FGC      老年代垃圾回收次数
              -FGCT    老年代垃圾回收时间
              -GCT      垃圾回收总消耗时间

-class     

监视类装载、卸载数量、总空间以及耗费的时间

参数解析:

              -Loaded        加载class的数量 
    -Bytes          class字节大小
    -Unloaded    未加载class的数量
    -Bytes          未加载class的字节大小
    -Time           加载时间

-gccapacity   

同-gc,不过还会输出Java堆各区域使用到的最大、最小空间。

jinfo(java配置信息工具)

jinfo(Configuration Info for Java)的作用是实时地查看和调整虚拟机各项参数。使用jps命令的-v参数可以查看虚拟机启动时显式指定的参数列表,但如果想知道未被显式指定的参数的系统默认值,就要用到jinfo命令了。

格式如下:

jinfo[option]pid

    -flag       输出虚拟机启动参数参数
    -sysprops      把虚拟机进程的System.getProperties()的内容打印出来

示例:

jmap(java内存映像工具)

jmap(Memory Map for Java)命令一般用于生成堆转储快照(一般称为deapdump或dump文件)。此命令用法较多,并不仅仅是为了获取dump文件,他还可以查询finalize执行队列,java堆和永久代的详细信息,如空间使用率,当前用的是那种收集器等。

什么是堆dump?

堆Dump是反应Java堆使用情况的内存镜像,其中主要包括系统信息、虚拟机属性、完整的线程Dump、所有类和对象的状态等。一般是内存不足、GC异常等情况下,我们就会换衣是内存泄漏。这个时候我们就可以制作堆Dump来查看具体情况,分析原因。

jmap[option]vmid

    -dump          生成java的转储快照
    -histo          显示堆中对象的统计信息,包括类、实例数量、合计数量等
    -heap          显示Java堆的详细信息,如使用哪种回收器、参数配置、分代情况等
    -clstats         以ClassLoader为统计口径显示永久代内存状态
    -finalizerinfo     显示再F-Queue中等待Finalizer线程执行finalizer方法的对象

常见用法如下:

1. jmap - heap pid 展示pid的整体堆信息

#ps -ef | grep java    #获取java的pid

[root@localhost ~]# jmap -heap 27900
Attaching to process ID 27900, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 20.45-b01
using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration: #堆内存初始化配置
   MinHeapFreeRatio = 40      #-XX:MinHeapFreeRatio设置JVM堆最小空闲比率  
   MaxHeapFreeRatio = 70      #-XX:MaxHeapFreeRatio设置JVM堆最大空闲比率  
   MaxHeapSize = 100663296 (96.0MB)   #-XX:MaxHeapSize=设置JVM堆的最大大小
   NewSize = 1048576 (1.0MB)     #-XX:NewSize=设置JVM堆的‘新生代’的默认大小
   MaxNewSize = 4294901760 (4095.9375MB) #-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
   OldSize = 4194304 (4.0MB)  #-XX:OldSize=设置JVM堆的‘老生代’的大小
   NewRatio = 2    #-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
   SurvivorRatio = 8  #-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值
   PermSize = 12582912 (12.0MB) #-XX:PermSize=<value>:设置JVM堆的‘持久代’的初始大小  
   MaxPermSize = 67108864 (64.0MB) #-XX:MaxPermSize=<value>:设置JVM堆的‘持久代’的最大大小  
Heap Usage:
New Generation (Eden + 1 Survivor Space): #新生代区内存分布,包含伊甸园区+1个Survivor区
   capacity = 30212096 (28.8125MB)
   used = 27103784 (25.848182678222656MB)
   free = 3108312 (2.9643173217773438MB)
   89.71169693092462% used
Eden Space: #Eden区内存分布
   capacity = 26869760 (25.625MB)
   used = 26869760 (25.625MB)
   free = 0 (0.0MB)
   100.0% used
From Space: #其中一个Survivor区的内存分布
   capacity = 3342336 (3.1875MB)
   used = 234024 (0.22318267822265625MB)
   free = 3108312 (2.9643173217773438MB)
   7.001809512867647% used
To Space: #另一个Survivor区的内存分布
   capacity = 3342336 (3.1875MB)
   used = 0 (0.0MB)
   free = 3342336 (3.1875MB)
   0.0% used
tenured generation:   #当前的Old区内存分布  
   capacity = 67108864 (64.0MB)
   used = 67108816 (63.99995422363281MB)
   free = 48 (4.57763671875E-5MB)
   99.99992847442627% used
Perm Generation:     #当前的 “持久代” 内存分布
   capacity = 14417920 (13.75MB)
   used = 14339216 (13.674942016601562MB)
   free = 78704 (0.0750579833984375MB)
   99.45412375710227% used

 

2. jmap - histo pid 展示class的内存情况

说明:instances(实例数)、bytes(大小)、class name(类名)。它基本是按照使用大小逆序排列。

   jmap -histo:live pid这个命令执行时,JVM会先触发gc,然后再统计信息。

从打印结果可以看出,类名中存在[C、[B 等内容,只知道他们占用了那么大的内存,但是不知道由什么对象创建的。下一步需要将其他dump出来,使用内存分析工具进一步明确是由谁引起的,由什么对象引起的。

jhat(虚拟机堆转储快照分析工具,和jmap搭配使用)

Sun JDK提供jhat(JVM Heap Analysis Tool)命令与jmap搭配使用,来分析jmap生成的堆转储快照。jhat内置了一个微型的HTTP/HTML服务器,生成dump文件的分析结果后,可以在浏览器中查看。然而实际中我们很少使用这个工具,因为一般服务器上设置的堆、栈内存都比较大,生成的dump也比较大,直接用jhat容易造成内存溢出,而是我们大部分会将对应的文件拷贝下来,通过其他可视化的工具进行分析。

使用方法:

        第一步:导出堆

        #jmap -dump:live,file=a.log pid

   第二步:分析对文件

   #jhat -J-Xmx512M a1.log

   说明:有时dump出来的堆很大,在启动时会报堆空间不足的错误,可加参数:jhat -J-Xmx512m <heap dump file>。这个内存大小可根据自己电脑进行设置。

   解析Java堆转储文件,并启动一个web server 。

   

    第三步:查看html,分析问题。

   在浏览器上输入http://ip:7000/

   

jstack(java堆栈跟踪工具)

   jstack(Stack Trace for Java)命令用于生成虚拟机当前时刻的线程快照(一般称为threaddump或者javacore文件)。线程快照就是当前㣈内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等等都是导致线程长时间停顿的常见原因。线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做些什么事情,或者等待着什么资源。

   格式如下:

jstack[option]vmid

    -F 当正常输出的请求不被相应时,强制输出线程堆栈
    -l 除堆栈外,显示关于锁的附加信息
    -m 如果调用到本地方法的话,可以显示C/C++的堆栈

    

使用方法:

        第一步:top命令:在linux环境下,通过top命令查看各个进程的cpu使用情况,默认按cpu使用率排序。

   

   1、上图中可以看出pid为23344的java进程占用了较多的cpu资源;
   2、通过命令top -Hp 23344 可以查看该进程下各个线程的cpu使用情况;

   

   上图中可以看出pid为25077的线程占了较多的cpu资源,利用jstack命令可以继续查看该线程当前的堆栈状态。

    第二步:jstack命令:通过top命令定位到cpu占用率较高的线程之后,继续使用jstack pid命令查看当前java进程的堆栈状态:

   

   jstack命令生成的thread dump信息包含了JVM中所有存活的线程,为了分析指定线程,必须找出对应线程的调用栈,但是如何找呢?

   在top命令中,已经获取到了占用cpu资源较高的线程pid,将该pid转成16进制的值,在thread dump中每个线程都有一个nid,找到对应的nid即可;隔一段时间再执行一次jstack命令获取thread dump,区分两份是否有差别,如上图中在nid=0x246c的线程调用栈中,发现该线程一直在执行JstackCase类第33行的calculate方法,得到这个信息,就可以检查对应的代码是否有问题。

   第三步:通过thread dump分析线程状态。

   除了上述的分析,大多数情况下会基于thead dump分析当前各个线程的运行情况,如是否存在死锁、是否存在一个线程长时间持有锁不放等。

   在dump中,线程一般存在如下几种状态:

   1、RUNNABLE,线程处于执行中

   2、BLOCKED,线程被阻塞

   3、WATTING,线程正在等待   

         实例1:多线程竞争synchronized锁

   

   如上图:线程1获取到锁,处于RUNNABLE状态,线程2处于BLOCK状态。

   1、locked <0x000000076bf62208>说明线程1对地址为0x000000076bf62208对象进行了加锁;
   2、waiting to lock <0x000000076bf62208> 说明线程2在等待地址为0x000000076bf62208对象上的锁;
   3、waiting for monitor entry [0x000000001e21f000]说明线程1是通过synchronized关键字进入了监视器的临界区,并处于"Entry Set"队列,等待monitor,具体实现可以参考深入分析synchronized的JVM实现

posted @ 2018-12-14 16:31  sssss-T  阅读(548)  评论(0)    收藏  举报