zoukankan      html  css  js  c++  java
  • JYM虚拟机性能监控与故障处理工具



    部分内容来源自:
    微信公众号:Java基基
    博客园: chen_hao:Java开发必须掌握的线上问题排查命令



    虚拟机性能监控工具,主要解决可能遇到的如下问题:

    • OutOfMemoryError,内存不足
    • 内存泄露
    • 线程死锁
    • 锁争用(Lock Contention)
    • Java进程消耗CPU过高

    主要的JDK监控和故障处理工具的名称和主要作用如下:

    名称 主要作用
    jps 显示指定系统内所有的HotSpot虚拟机进程
    jstat 用于收集HotSpot虚拟机各方面的运行数据
    jinfo 显示虚拟机配置信息
    jamp 生成虚拟机的内存转储快照文件
    jhat 用于分析heapdump文件,它会建立一个HTTP/HTML服务器,让用户可以在浏览器上查看分析结果
    jstack 显示虚拟机的线程快照

    一、jps:虚拟机进程状况工具

    列出正在运行的虚拟机进程,并显示虚拟机执行主类(main class,main()函数所在的类)名称以及这些进程的本地虚拟机唯一ID。

    jps命令格式:

    jps [options] [hostid]

    常用指令

    jps:显示当前用户的所有java进程的PID
    
    jps -v 3331:显示虚拟机参数
    
    jps -m 3331:显示传递给main()函数的参数
    
    jps -l 3331:显示主类的全路径
    

    jps可以通过RMI协议查询开启了RMI服务的远程虚拟机进程状态,hostid为RMI注册表中注册的主机名。

    jps工具主要选型:

    选型 作用
    -q 只输出LVMID,省略主类的名称
    -m 输出虚拟机进程启动时传递给主类main()函数的参数
    -l 输出主类的全名,如果进程执行的是Jar包,输出jar路径
    -v 输出虚拟机进程启动时的JYM参数

    二、jstat:虚拟机统计信息监视工具

    用于监视虚拟机各种运行状态信息的命令工具。它可以显示本地或远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。

    jstat命令格式:

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

    jstat [ generalOption | outputOptions vmid [interval[s|ms] [count]] ]

    常用指令

    jstat -gc 3331 250 20 :查询进程2764的垃圾收集情况,每250毫秒查询一次,一共查询20次。
    
    jstat -gccause:额外输出上次GC原因
    
    jstat -calss:件事类装载、类卸载、总空间以及所消耗的时间
    

    vmid是Java虚拟机ID,在Linux/Unix系统上一般就是进程ID。interval是采样时间间隔。count是采样数目。比如下面输出的是GC信息,采样时间间隔为250ms,采样数为4:

    root@ubuntu:/# jstat -gc 21711 250 4
    S0C    S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT     GCT
    192.0  192.0   64.0   0.0    6144.0   1854.9   32000.0     4111.6   55296.0 25472.7    702    0.431   3      0.218    0.649
    192.0  192.0   64.0   0.0    6144.0   1972.2   32000.0     4111.6   55296.0 25472.7    702    0.431   3      0.218    0.649
    192.0  192.0   64.0   0.0    6144.0   1972.2   32000.0     4111.6   55296.0 25472.7    702    0.431   3      0.218    0.649
    192.0  192.0   64.0   0.0    6144.0   2109.7   32000.0     4111.6   55296.0 25472.7    702    0.431   3      0.218    0.649
    

    JVM堆内存布局:
    在这里插入图片描述

    可以看出:

    堆内存 = 年轻代 + 年老代 + 永久代
    年轻代 = Eden区 + 两个Survivor区(From和To)
    
    S0C、S1C、S0U、S1U:Survivor 0/1区容量(Capacity)和使用量(Used)
    EC、EU:Eden区容量和使用量
    OC、OU:年老代容量和使用量
    PC、PU:永久代容量和使用量
    YGC、YGT:年轻代GC次数和GC耗时
    FGC、FGCT:Full GC次数和Full GC耗时
    GCT:GC总耗时
    

    三、jinfo:配置信息工具

    jdk8中已经不支持该命令。

    实时的查看和调整虚拟机各项参数。

    四、jmap:Java内存映像工具

    用于生成堆转储快照。

    jmap用来查看堆内存使用状况,一般结合jhat使用。

    jmap语法格式如下:

    jmap [option] pid
    jmap [option] executable core
    jmap [option] [server-id@]remote-hostname-or-ip
    

    常用指令

    jmap -heap 3331:查看java 堆(heap)使用情况
    
    jmap -histo 3331:查看堆内存(histogram)中的对象数量及大小
    
    jmap -histo:live 3331:JVM会先触发gc,然后再统计信息
    
    jmap -dump:format=b,file=heapDump 3331:将内存使用的详细情况输出到文件,之后一般使用其他工具进行分析。
    

    如果运行在64位JVM上,可能需要指定-J-d64命令选项参数。

    jmap -permstat pid
    

    打印进程的类加载器和类加载器加载的持久代对象信息,输出:类加载器名称、对象是否存活(不可靠)、对象地址、父类加载器、已加载的类大小等信息,如下图:
    在这里插入图片描述
    使用jmap -heap pid查看进程堆内存使用情况,包括使用的GC算法、堆配置参数和各代中堆内存使用情况。比如下面的例子:

    root@ubuntu:/# jmap -heap 21711
    Attaching to process ID 21711, please wait...
    Debugger attached successfully.
    Server compiler detected.
    JVM version is 20.10-b01
    
    using thread-local object allocation.
    Parallel GC with 4 thread(s)
    
    Heap Configuration:
    MinHeapFreeRatio = 40
    MaxHeapFreeRatio = 70
    MaxHeapSize      = 2067791872 (1972.0MB)
    NewSize          = 1310720 (1.25MB)
    MaxNewSize       = 17592186044415 MB
    OldSize          = 5439488 (5.1875MB)
    NewRatio         = 2
    SurvivorRatio    = 8
    PermSize         = 21757952 (20.75MB)
    MaxPermSize      = 85983232 (82.0MB)
    
    Heap Usage:
    PS Young Generation
    Eden Space:
       capacity = 6422528 (6.125MB)
       used     = 5445552 (5.1932830810546875MB)
       free     = 976976 (0.9317169189453125MB)
       84.78829520089286% used
    From Space:
       capacity = 131072 (0.125MB)
       used     = 98304 (0.09375MB)
       free     = 32768 (0.03125MB)
       75.0% used
    To Space:
       capacity = 131072 (0.125MB)
       used     = 0 (0.0MB)
       free     = 131072 (0.125MB)
       0.0% used
    PS Old Generation
       capacity = 35258368 (33.625MB)
       used     = 4119544 (3.9287033081054688MB)
       free     = 31138824 (29.69629669189453MB)
       11.683876009235595% used
    PS Perm Generation
       capacity = 52428800 (50.0MB)
       used     = 26075168 (24.867218017578125MB)
       free     = 26353632 (25.132781982421875MB)
       49.73443603515625% used
       ....
    

    使用jmap -histo[:live] pid查看堆内存中的对象数目、大小统计直方图,如果带上live则只统计活对象,如下:

    root@ubuntu:/# jmap -histo:live 21711 | more
    num     #instances         #bytes  class name----------------------------------------------
       1:         38445        5597736  <constMethodKlass>
       2:         38445        5237288  <methodKlass>
       3:          3500        3749504  <constantPoolKlass>
       4:         60858        3242600  <symbolKlass>
       5:          3500        2715264  <instanceKlassKlass>
       6:          2796        2131424  <constantPoolCacheKlass>
       7:          5543        1317400  [I
       8:         13714        1010768  [C
       9:          4752        1003344  [B
      10:          1225         639656  <methodDataKlass>
      11:         14194         454208  java.lang.String
      12:          3809         396136  java.lang.Class
      13:          4979         311952  [S
      14:          5598         287064  [[I
      15:          3028         266464  java.lang.reflect.Method
      16:           280         163520  <objArrayKlassKlass>
      17:          4355         139360  java.util.HashMap$Entry
      18:          1869         138568  [Ljava.util.HashMap$Entry;
      19:          2443          97720  java.util.LinkedHashMap$Entry
      20:          2072          82880  java.lang.ref.SoftReference
      21:          1807          71528  [Ljava.lang.Object;
      22:          2206          70592  java.lang.ref.WeakReference
      23:           934          52304  java.util.LinkedHashMap
      24:           871          48776  java.beans.MethodDescriptor
      25:          1442          46144  java.util.concurrent.ConcurrentHashMap$HashEntry
      26:           804          38592  java.util.HashMap
      27:           948          37920  java.util.concurrent.ConcurrentHashMap$Segment
      28:          1621          35696  [Ljava.lang.Class;
      29:          1313          34880  [Ljava.lang.String;
      30:          1396          33504  java.util.LinkedList$Entry
      31:           462          33264  java.lang.reflect.Field
      32:          1024          32768  java.util.Hashtable$Entry
      33:           948          31440  [Ljava.util.concurrent.ConcurrentHashMap$HashEntry;
    

    class name是对象类型,说明如下:

    B  byte
    C  char
    D  double
    F  float
    I  int
    J  long
    Z  boolean
    [  数组,如[I表示int[]
    [L+类名 其他对象
    

    还有一个很常用的情况是:用jmap把进程内存使用情况dump到文件中,再用jhat分析查看。jmap进行dump命令格式如下:

    jmap -dump:format=b,file=dumpFileName pid
    

    对上面进程ID为21711进行Dump:

    root@ubuntu:/# jmap -dump:format=b,file=/tmp/dump.dat 21711
    Dumping heap to /tmp/dump.dat ...
    Heap dump file created
    

    dump出来的文件可以用MAT、VisualVM等工具查看,这里用jhat查看:

    root@ubuntu:/# jhat -port 9998 /tmp/dump.dat
    Reading from /tmp/dump.dat...
    Dump file created Tue Jan 28 17:46:14 CST 2014Snapshot read, resolving...
    Resolving 132207 objects...
    Chasing references, expect 26 dots..........................
    Eliminating duplicate references..........................
    Snapshot resolved.
    Started HTTP server on port 9998Server is ready.
    

    注意如果Dump文件太大,可能需要加上-J-Xmx512m这种参数指定最大堆内存,即jhat -J-Xmx512m -port 9998 /tmp/dump.dat。然后就可以在浏览器中输入主机地址:9998查看了:
    在这里插入图片描述

    五、jhat:虚拟机堆转储快照分析工具

    一般与jmap搭配使用,用来分析jmap生成的堆转储文件。

    常用指令

    jmap -dump:format=b,file=heapDump 3331 + jhat heapDump:解析Java堆转储文件,并启动一个 web server
    

    六、jstack:Java堆栈跟踪工具

    jstack主要用来查看某个Java进程内的线程堆栈信息。语法格式如下:

    jstack [option] pid
    jstack [option] executable core
    jstack [option] [server-id@]remote-hostname-or-ip
    

    命令行参数选项说明如下:

    -l long listings,会打印出额外的锁信息,在发生死锁时可以用jstack -l pid来观察锁持有情况-m mixed mode,不仅会输出Java堆栈信息,还会输出C/C++堆栈信息(比如Native方法)

    常用指令

    jstack 3331:查看线程情况
    
    jstack -F 3331:正常输出不被响应时,使用该指令
    
    jstack -l 3331:除堆栈外,显示关于锁的附件信息
    
    Jstack -l PID >> 123.txt  在发生死锁时可以用jstack -l pid来观察锁持有情况 >>123.txt dump到指定文件
    

    jstack可以定位到线程堆栈,根据堆栈信息我们可以定位到具体代码,所以它在JVM性能调优中使用得非常多。下面我们来一个实例找出某个Java进程中最耗费CPU的Java线程并定位堆栈信息,用到的命令有ps、top、printf、jstack、grep。

    第一步先找出Java进程ID,我部署在服务器上的Java应用名称为mrf-center:

    root@ubuntu:/# ps -ef | grep mrf-center | grep -v grep
    root     21711     1  1 14:47 pts/3    00:02:10 java -jar mrf-center.jar
    

    得到进程ID为21711,第二步找出该进程内最耗费CPU的线程,可以使用ps -Lfp pid或者ps -mp pid -o THREAD, tid, time或者top -Hp pid,我这里用第三个,输出如下:
    在这里插入图片描述
    TIME列就是各个Java线程耗费的CPU时间,CPU时间最长的是线程ID为21742的线程,用

    printf "%x
    " 21742
    

    得到21742的十六进制值为54ee,下面会用到。

    OK,下一步终于轮到jstack上场了,它用来输出进程21711的堆栈信息,然后根据线程ID的十六进制值grep,如下:

    root@ubuntu:/# jstack 21711 | grep 54ee
    "PollIntervalRetrySchedulerThread" prio=10 tid=0x00007f950043e000 nid=0x54ee in Object.wait() [0x00007f94c6eda000]
    

    可以看到CPU消耗在PollIntervalRetrySchedulerThread这个类的Object.wait(),我找了下我的代码,定位到下面的代码:

    // Idle wait
    getLog().info("Thread [" + getName() + "] is idle waiting...");
    schedulerThreadState = PollTaskSchedulerThreadState.IdleWaiting;
    long now = System.currentTimeMillis();
    long waitTime = now + getIdleWaitTime();
    long timeUntilContinue = waitTime - now;
    synchronized(sigLock) {    try {
            if(!halted.get()) {
                sigLock.wait(timeUntilContinue);
            }
        }     catch (InterruptedException ignore) {
        }
    }
    

    它是轮询任务的空闲等待代码,上面的sigLock.wait(timeUntilContinue)就对应了前面的Object.wait()。

    七、HSDIS:JIT生成代码反汇编

    八、hprof(Heap/CPU Profiling Tool)

    hprof能够展现CPU使用率,统计堆内存使用情况。

    语法格式如下:

    java -agentlib:hprof[=options] ToBeProfiledClass
    java -Xrunprof[:options] ToBeProfiledClass
    javac -J-agentlib:hprof[=options] ToBeProfiledClass
    

    完整的命令选项如下:

    Option Name and Value  Description                    Default
    ---------------------  -----------                    -------
    heap=dump|sites|all    heap profiling                 all
    cpu=samples|times|old  CPU usage                      off
    monitor=y|n            monitor contention             n
    format=a|b             text(txt) or binary output     a
    file=<file>            write data to file             java.hprof[.txt]
    net=<host>:<port>      send data over a socket        off
    depth=<size>           stack trace depth              4
    interval=<ms>          sample interval in ms          10
    cutoff=<value>         output cutoff point            0.0001
    lineno=y|n             line number in traces?         y
    thread=y|n             thread in traces?              n
    doe=y|n                dump on exit?                  y
    msa=y|n                Solaris micro state accounting n
    force=y|n              force output to <file>         y
    verbose=y|n            print messages about dumps     y
    

    九、常见问题定位过程

    9.1频繁GC问题或内存溢出问题

    一、使用jps查看线程ID

    二、使用jstat -gc 3331 250 20 查看gc情况,一般比较关注PERM区的情况,查看GC的增长情况。

    三、使用jstat -gccause:额外输出上次GC原因

    四、使用jmap -dump:format=b,file=heapDump 3331生成堆转储文件

    五、使用jhat或者可视化工具(Eclipse Memory Analyzer 、IBM HeapAnalyzer)分析堆情况。

    六、结合代码解决内存溢出或泄露问题。

    9.2死锁问题

    一、使用jps查看线程ID

    二、使用jstack 3331:查看线程情况

  • 相关阅读:
    每日日报2020.12.1
    每日日报2020.11.30
    981. Time Based Key-Value Store
    1146. Snapshot Array
    565. Array Nesting
    79. Word Search
    43. Multiply Strings
    Largest value of the expression
    1014. Best Sightseeing Pair
    562. Longest Line of Consecutive One in Matrix
  • 原文地址:https://www.cnblogs.com/aixing/p/13327424.html
Copyright © 2011-2022 走看看