美文网首页
扫地僧都会的关于JVM分析那些事儿

扫地僧都会的关于JVM分析那些事儿

作者: 南宋临安府 | 来源:发表于2018-03-06 20:10 被阅读0次

    “不尚贤,使民不争;不贵难得之货,使民不为盗;不见可欲,使民心不乱。
    是以圣人之治,虚其心,实其腹,弱其志,强其骨,常使民无知无欲。
    使夫知者不敢为也。
    为无为,则无不治。”[1]

    JVM为我们提供了很多工具,我们可以用它来监测运行中的程序,对于程序优化和问题分析很有帮助,今天就聊聊这方面的操作。

    大致涉及如下内容:

    • jps
    • jstack
    • jstat
    • jmap
    • 实战

    jps

    jps命令可以显示当前运行java进程以及相关参数。

    man jps  //可以命令行jps的用法说明
    
    jps -q //只显示pid,不显示class名称,jar文件名和传递给main 方法的参数
    
    jps -m //输出传递给main 方法的参数
    
    jps -l //输出应用程序main class的完整package名 或者 应用程序的jar文件完整路径名
    
    jps -v 输出传递给JVM的参数
    

    jstack

    jstack是虚拟机堆栈追踪工具,用于生成虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。

    线程状态:

    NEW 未启动的
    RUNNABLE 在虚拟中运行的
    BLOCKED 受阻塞并等待监视器锁
    WAITTING 无限期等待另一个线程执行特定操作
    TIMED_WAITTING 有限期的等待另一线程执行特定操作
    TERMINATED 已退出的
    
    jstack [options] <pid> //pid可以通过jps或者top获取,后面给出的具体分析实例会写到
    

    [options]:

    jstack -F 当’jstack [-l] pid’没有相应的时候强制打印栈信息
    
    jstack -l 长列表. 打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表.
    
    jstack -m 打印java和native c/c++框架的所有栈信息.
    
    jstack -h 打印帮助信息
    

    jstat

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

    jstat [options] <pdi>
    

    [options]:

    –class 监视类装载、卸载数量、总空间及类装载所耗费的时间 
    –gc 监视Java堆状况,包括Eden区、2个Survivor区、老年代、永久代等的容量 
    –gccapacity 监视内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大和最小空间 
    –gcutil 监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比 
    –gccause 与-gcutil功能一样,但是会额外输出导致上一次GC产生的原因 –gcnew 监视新生代GC的状况 
    –gcnewcapacity 监视内容与-gcnew基本相同,输出主要关注使用到的最大和最小空间 
    –gcold 监视老年代GC的状况 
    –gcoldcapacity 监视内容与——gcold基本相同,输出主要关注使用到的最大和最小空间 
    –gcpermcapacity 输出永久代使用到的最大和最小空间 
    –compiler 输出JIT编译器编译过的方法、耗时等信息
    –printcompilation 输出已经被JIT编译的方法
    
    jstat -class <pid> 显示加载class的数量,及所占空间等信息。
    
    结果说明:
    Loaded 装载的类的数量 
    Bytes 装载类所占用的字节数 
    Unloaded 卸载类的数量 
    Bytes 卸载类的字节数 
    Time 装载和卸载类所花费的时间
    
    jstat -gc <pid>: 可以显示gc的信息,查看gc的次数,及时间。
    
    结果说明:
    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)
    

    jmap

    jmap是JDK自带的工具软件,主要用于打印指定Java进程(或核心文件、远程调试服务器)的共享对象内存映射或堆内存细节。堆Dump是反应Java堆使用情况的内存镜像,其中主要包括系统信息、虚拟机属性、完整的线程Dump、所有类和对象的状态等。
    常见内存错误:

    OutOfMemoryError 年老代内存不足。
    OutOfMemoryError:PermGen Space 永久代内存不足。
    OutOfMemoryError:GC overhead limit exceed 垃圾回收时间占用系统运行时间的98%或以上。
    
    jmap [optioins] <pid>
    

    [options]

    <no option> 如果使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每个共享对象的起始地址、映射大小以及共享对象文件的路径全称。这与Solaris的pmap工具比较相似。
    -dump:[live,]format=b,file=<filename> 以hprof二进制格式转储Java堆到指定filename的文件中。live子选项是可选的。如果指定了live子选项,堆中只有活动的对象会被转储。想要浏览heap dump,你可以使用jhat(Java堆分析工具)读取生成的文件。
    -finalizerinfo 打印等待终结的对象信息。
    -heap 打印一个堆的摘要信息,包括使用的GC算法、堆配置信息和generation wise heap usage。
    -histo[:live] 打印堆的柱状图。其中包括每个Java类、对象数量、内存大小(单位:字节)、完全限定的类名。打印的虚拟机内部的类名称将会带有一个’*’前缀。如果指定了live子选项,则只计算活动的对象。
    -permstat 打印Java堆内存的永久保存区域的类加载器的智能统计信息。对于每个类加载器而言,它的名称、活跃度、地址、父类加载器、它所加载的类的数量和大小都会被打印。此外,包含的字符串数量和大小也会被打印。
    -F 强制模式。如果指定的pid没有响应,请使用jmap -dump或jmap -histo选项。此模式下,不支持live子选项。
    -h 打印帮助信息。
    -help 打印帮助信息。
    -J<flag> 指定传递给运行jmap的JVM的参数。
    

    实战

    下面以一个简单的例子,分析出运行项目中最消耗cpu的线程并定位代码。

    • 得到进程id
      jps or ps,以ps为例:
    ps -ef | grep Kernel | grep -v grep
    

    得到进程id 即pid
    然后查找该进程id下各个线程中最消耗的那个线程tid,用top指令:

    top -Hp <pid>
    

    得到线程id 即tid,由于jstack中tid和nid都是十六进制的,因此需要将得到的tid转换为十六进制:

    printf '%x\n' <tid>
    

    将得到的十六进制tid,用jstack即可定位具体线程:

    jstack <pid> | grep <tid十六进制>
    

    1. 老子《道德经》第三章,老子故里,中国鹿邑。

    相关文章

      网友评论

          本文标题:扫地僧都会的关于JVM分析那些事儿

          本文链接:https://www.haomeiwen.com/subject/banlfftx.html