• 关于JVM——工具(一)


    一.Jps

    1、概述

    jps(Java Virtual Machine Process Status Tool)是JDK 1.5提供的一个显示当前所有java进程pid的命令,简单实用,非常适合在linux/unix平台上简单察看当前java进程的一些简单情况。

    jps存放在JAVA_HOME/bin/jps,使用时为了方便请将JAVA_HOME/bin/加入到Path。

    jps 命令类似与 linux 的 ps 命令,但是它只列出系统中所有的 Java 应用程序。 通过 jps 命令可以方便地查看 Java 进程的启动类、传入参数和 Java 虚拟机参数等信息。

    如果在 linux 中想查看 java 的进程,一般我们都需要 ps -ef | grep java 来获取进程 ID。

    如果只想获取 Java 程序的进程,可以直接使用 jps 命令来直接查看。

    2、原理

    java程序在启动以后,会在java.io.tmpdir指定的目录下,就是临时文件夹里,生成一个类似于hsperfdata_User的文件夹,

    这个文件夹里(在Linux中为/tmp/hsperfdata_{userName}/),有几个文件,名字就是java进程的pid,因此列出当前运行的java进程,只是把这个目录里的文件名列一下而已。

    至于系统的参数什么,就可以解析这几个文件获得。

    window系统显示如下:

    3、基本用法

    (1)参数说明

    -q:只输出进程 ID

    -m:输出传入 main 方法的参数

    -l:输出完全的包名,应用主类名,jar的完全路径名

    -v:输出jvm参数

    -V:输出通过flag文件传递到JVM中的参数

    [hostid]:远程服务器地址,jps 支持远程调用
    [protocol:][[//]hostname][:port][/servername]

    示例一:jps

    无参数:显示进程的ID 和 类的名称

     jps 不带参数,默认显示 进程ID 和 启动类的名称。

    示例二:jps -q

    参数 -q 只输出进程ID,而不显示出类的名称

    示例三:jps -m

    参数 -m 可以输出传递给 Java 进程(main 方法)的参数。

    示例五:jps -v

    参数 -v 可以显示传递给 Java 虚拟机的参数。

    4、获取远程服务器 jps 信息

    jps 支持查看远程服务上的 jvm 进程信息。如果需要查看其他机器上的 jvm 进程,需要在待查看机器上启动 jstatd 服务。

    开启 jstatd 服务

    启动 jstatd 服务,需要有足够的权限。 需要使用 Java 的安全策略分配相应的权限。

    创建 jstatd.all.policy 策略文件。

    grant codebase "file:${java.home}/../lib/tools.jar" {
        permission java.security.AllPermission;
    };

    启动 jstatd 服务器:

    jstatd -J-Djava.security.policy=jstatd.all.policy -J-Djava.rmi.server.hostname=192.168.31.241

    -J 参数是一个公共的参数,如 jps、 jstat 等命令都可以接收这个参数。 由于 jps、 jstat 命令本身也是 Java 应用程序, -J 参数可以为 jps 等命令本身设置 Java 虚拟机参数。

    -Djava.security.policy:指定策略文件

    -Djava.rmi.server.hostname:指定服务器的ip地址(可忽略)

    默认情况下, jstatd 开启在 1099 端口上开启 RMI 服务器。

    二.Jinfo

    1、概述

    jinfo(Java Configuration Info)是 JDK 自带的命令,可以用来查看正在运行的 java 应用程序的扩展参数,包括Java System属性和JVM命令行参数;

    也可以动态的修改正在运行的 JVM 一些参数。当系统崩溃时,jinfo可以从core文件里面知道崩溃的Java应用程序的配置信息。

    2、Javacore 概述

    Javacore,也可以称为“threaddump”或是“javadump”,它是 Java 提供的一种诊断特性,能够提供一份可读的当前运行的 JVM 中线程使用情况的快照。

    即在某个特定时刻,JVM 中有哪些线程在运行,每个线程执行到哪一个类,哪一个方法。

    应用程序如果出现不可恢复的错误或是内存泄露,就会自动触发 Javacore 的生成。

    3、基本用法

    示例一: no option

    命令:jinfo pid

    描述:输出当前 jvm 进程的全部参数和系统属性

    示例二: -flag name

    命令:jinfo -flag name pid

    描述:输出对应名称的参数

    使用该命令,可以查看指定的 jvm 参数的值。如:查看当前 jvm 进程是否开启打印 GC 日志。

    示例三:-flag [+|-]name

    命令:jinfo -flag [+|-]name pid

    描述:开启或者关闭对应名称的参数

    使用 jinfo 可以在不重启虚拟机的情况下,可以动态的修改 jvm 的参数。尤其在线上的环境特别有用。

    使用如下:

    示例四:-flag name=value

    命令:jinfo -flag name=value pid

    描述:修改指定参数的值。

    同示例三,但示例三主要是针对 boolean 值的参数设置的。

    如果是设置 value值,则需要使用 name=value 的形式。

    使用如下:

    jinfo虽然可以在java程序运行时动态地修改虚拟机参数,但并不是所有的参数都支持动态修改

    示例五: -flags

    命令:jinfo -flags pid

    描述:输出全部的参数

     

    示例六:-sysprops

    命令:jinfo -sysprops pid

    描述:输出当前 jvm 进行的全部的系统属性

    三.Jmap

    1、概述

    jmap命令是一个可以输出所有内存中对象的工具,甚至可以将VM 中的heap,以二进制输出成文本。

    打印出某个java进程(使用pid)内存内的,所有‘对象’的情况(如:产生那些对象,及其数量)。

    可以获得运行中的jvm的堆的快照,从而可以离线分析堆,以检查内存泄漏,检查一些严重影响性能的大对象的创建,检查系统中什么对象最多,各种对象所占内存的大小等等。

    当服务发生GC问题时,一般会使用jmap工具进行分析。

    作用:

    (1)查看堆各个对象的数量,大小;

    (2)dump堆里的对象信息,然后可以用MAT分析;

    (3)查看堆的配置情况和GC算法;

    (4)查看堆永久代信息;

    2、基本用法

    示例一:no option

    命令:jmap pid

    描述:查看进程的内存映像信息,类似 Solaris pmap 命令。

    使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每个共享对象的起始地址、映射大小以及共享对象文件的路径全称。这与Solaris的pmap工具比较相似。

    示例二:heap

    命令:jmap -heap pid

    描述:显示Java堆详细信息

    打印一个堆的摘要信息,包括使用的GC算法、堆配置信息和各内存区域内存使用信息:

    C:Usersjjs>jmap -heap 5932
    Attaching to process ID 5932, please wait...
    Debugger attached successfully.
    Server compiler detected.
    JVM version is 25.91-b15
    
    using thread-local object allocation.
    Parallel GC with 4 thread(s)
    
    Heap Configuration:
       MinHeapFreeRatio         = 0
       MaxHeapFreeRatio         = 100
       MaxHeapSize              = 1073741824 (1024.0MB)
       NewSize                  = 42991616 (41.0MB)
       MaxNewSize               = 357564416 (341.0MB)
       OldSize                  = 87031808 (83.0MB)
       NewRatio                 = 2
       SurvivorRatio            = 8
       MetaspaceSize            = 21807104 (20.796875MB)
       CompressedClassSpaceSize = 1073741824 (1024.0MB)
       MaxMetaspaceSize         = 17592186044415 MB
       G1HeapRegionSize         = 0 (0.0MB)
    
    Heap Usage:
    PS Young Generation
    Eden Space:
       capacity = 60293120 (57.5MB)
       used     = 44166744 (42.120689392089844MB)
       free     = 16126376 (15.379310607910156MB)
       73.25337285580842% used
    From Space:
       capacity = 5242880 (5.0MB)
       used     = 0 (0.0MB)
       free     = 5242880 (5.0MB)
       0.0% used
    To Space:
       capacity = 14680064 (14.0MB)
       used     = 0 (0.0MB)
       free     = 14680064 (14.0MB)
       0.0% used
    PS Old Generation
       capacity = 120061952 (114.5MB)
       used     = 19805592 (18.888084411621094MB)
       free     = 100256360 (95.6119155883789MB)
       16.496143590935453% used
    
    20342 interned Strings occupying 1863208 bytes.

    示例三:histo[:live]

    命令:jmap -histo:live pid

    描述:显示堆中对象的统计信息

    其中包括每个Java类、对象数量、内存大小(单位:字节)、完全限定的类名。打印的虚拟机内部的类名称将会带有一个’*’前缀。如果指定了live子选项,则只计算活动的对象。

    示例四:clstats

    命令:jmap -clstats pid

    描述:打印类加载器信息

    -clstats是-permstat的替代方案,在JDK8之前,-permstat用来打印类加载器的数据

    打印Java堆内存的永久保存区域的类加载器的智能统计信息。对于每个类加载器而言,它的名称、活跃度、地址、父类加载器、它所加载的类的数量和大小都会被打印。

    此外,包含的字符串数量和大小也会被打印。

    示例五:finalizerinfo

    命令:jmap -finalizerinfo pid

    描述:打印等待终结的对象信息。

    Number of objects pending for finalization: 0 说明当前F-QUEUE队列中并没有等待Fializer线程执行final

    示例六:dump:<dump-options>

    命令:jmap -dump:format=b,file=heapdump.phrof pid

    描述:生成堆转储快照dump文件。

    以hprof二进制格式转储Java堆到指定filename的文件中。live子选项是可选的。如果指定了live子选项,堆中只有活动的对象会被转储。

    想要浏览heap dump,你可以使用jhat(Java堆分析工具)读取生成的文件。

    这个命令执行,JVM会将整个heap的信息dump写入到一个文件,heap如果比较大的话,就会导致这个过程比较耗时,并且执行的过程中为了保证dump的信息是可靠的,所以会暂停应用, 线上系统慎用。

    生成Heap Dump文件的方法:

    JMAP(Java Memory Map)

    方法一:让运行中的JVM生成Dump文件

    /usr/java/jdk/bin/jmap -F -dump:format=b,file=/path/to/heap/dump/heap.bin PID

    方法二:让JVM在遇到OOM(OutOfMemoryError)时生成Dump文件

    -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/heap/dump

    用MAT分析Dump文件

    MAT(Memory Analyzer)

    http://www.eclipse.org/mat/

    Mat是eclipse插件,要安装它.路径可到 http://www.eclipse.org/mat/downloads.php 找

    如:http://download.eclipse.org/mat/1.8/update-site/

    安装完插件后,eclipse的file菜单有个 Open Heap Dump最后能生成一个很直观的图

    见图:

    四.Jstack

    1、概述

    jstack能得到运行java程序的java stack和native stack的信息。可以轻松得知当前线程的运行情况。

    jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项"-J-d64"。

    Windows的jstack使用方式只支持以下的这种方式:jstack [-l] pid

    如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。

    另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。

    需要注意的问题:

    (1)不同的 JAVA虚机的线程 DUMP的创建方法和文件格式是不一样的,不同的 JVM版本, dump信息也有差别。

    (2)在实际运行中,往往一次 dump的信息,还不足以确认问题。建议产生三次 dump信息,如果每次 dump都指向同一个问题,才确定问题的典型性。 

    jstack是java虚拟机自带的一种堆栈跟踪工具。

    jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,

    如线程间死锁、死循环、请求外部资源导致的长时间等待等。

    线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。

    如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。

    另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。

    2、例子——检测死锁

    示例代码

    public class DeadLock implements Runnable {
    
        private int flag;
    
        private static final Object a = new Object();
        private static final Object b = new Object();
    
        public DeadLock(int flag) {
            this.flag = flag;
        }
    
        @Override
        public void run() {
    
            if (flag == 1) {
                synchronized (a) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("当前线程:"+Thread.currentThread().getName()+"尝试获取b");
                    synchronized (b) {
                        System.out.println("当前线程:"+Thread.currentThread().getName()+"获得了b");
                    }
                }
            } else {
                synchronized (b) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("当前线程:"+Thread.currentThread().getName()+"尝试获取a");
                    synchronized (a) {
                        System.out.println("当前线程:"+Thread.currentThread().getName()+"等待获得a");
                    }
                }
            }
    
        }
    }
    public class DeadLockLauncher {
    
        public static void main(String[] args) {
    
            final DeadLock deadLock1 = new DeadLock(1);
            final DeadLock deadLock2 = new DeadLock(0);
    
            Thread t1 = new Thread(deadLock1, "线程1");
            Thread t2 = new Thread(deadLock2, "线程2");
            t1.start();
            t2.start();
        }
    }

    打开命令行窗口,输入 jps

    C:Usersheyfan.xie>jps
    16592 Jps
    17232 DeadLockLauncher
    7872
    17092 Launcher
    14040 Launcher

    死锁启动类ID为 17232, 输入 jstack -F 17232

    C:Usersheyfan.xie>jstack -F 17232
    Attaching to process ID 17232, please wait...
    Debugger attached successfully.
    Server compiler detected.
    JVM version is 25.92-b14
    Deadlock Detection:
    
    Found one Java-level deadlock:
    =============================
    
    "线程2":
      waiting to lock Monitor@0x000000001f1e0288 (Object@0x000000076ba3f420, a java/lang/Object),
      which is held by "线程1"
    "线程1":
      waiting to lock Monitor@0x000000001f1e1728 (Object@0x000000076ba3f430, a java/lang/Object),
      which is held by "线程2"
    
    Found a total of 1 deadlock.
    
    Thread 3: (state = BLOCKED)
    
    
    Thread 22: (state = BLOCKED)
     - cn.cici.frigate.logistics.dead.DeadLock.run() @bci=120, line=41 (Interpreted frame)
     - java.lang.Thread.run() @bci=11, line=745 (Interpreted frame)
    
    
    Thread 21: (state = BLOCKED)
     - cn.cici.frigate.logistics.dead.DeadLock.run() @bci=34, line=30 (Interpreted frame)
     - java.lang.Thread.run() @bci=11, line=745 (Interpreted frame)
    
    
    Thread 15: (state = IN_NATIVE)
     - java.net.SocketInputStream.socketRead0(java.io.FileDescriptor, byte[], int, int, int) @bci=0 (Interpreted frame)
     - java.net.SocketInputStream.socketRead(java.io.FileDescriptor, byte[], int, int, int) @bci=8, line=116 (Interpreted frame)
    
    // 省略更多的信息

    可以看到* Found a total of 1 deadlock* 竞争锁定的对象为 java/lang/Object

    3、例子——检测cpu高

    步骤一:查看cpu占用高进程

    top
    
    Mem:  16333644k total,  9472968k used,  6860676k free,   165616k buffers
    Swap:        0k total,        0k used,        0k free,  6665292k cached
    
      PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND     
    17850 root      20   0 7588m 112m  11m S 100.7  0.7  47:53.80 java       
     1552 root      20   0  121m  13m 8524 S  0.7  0.1  14:37.75 AliYunDun   
     3581 root      20   0 9750m 2.0g  13m S  0.7 12.9 298:30.20 java        
        1 root      20   0 19360 1612 1308 S  0.0  0.0   0:00.81 init        
        2 root      20   0     0    0    0 S  0.0  0.0   0:00.00 kthreadd    
        3 root      RT   0     0    0    0 S  0.0  0.0   0:00.14 migration/0 

    步骤二:查看cpu占用高线程

    top -H -p 17850
    
    top - 17:43:15 up 5 days,  7:31,  1 user,  load average: 0.99, 0.97, 0.91
    Tasks:  32 total,   1 running,  31 sleeping,   0 stopped,   0 zombie
    Cpu(s):  3.7%us,  8.9%sy,  0.0%ni, 87.4%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
    Mem:  16333644k total,  9592504k used,  6741140k free,   165700k buffers
    Swap:        0k total,        0k used,        0k free,  6781620k cached
    
      PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
    17880 root      20   0 7588m 112m  11m R 99.9  0.7  50:47.43 java
    17856 root      20   0 7588m 112m  11m S  0.3  0.7   0:02.08 java
    17850 root      20   0 7588m 112m  11m S  0.0  0.7   0:00.00 java
    17851 root      20   0 7588m 112m  11m S  0.0  0.7   0:00.23 java
    17852 root      20   0 7588m 112m  11m S  0.0  0.7   0:02.09 java
    17853 root      20   0 7588m 112m  11m S  0.0  0.7   0:02.12 java
    17854 root      20   0 7588m 112m  11m S  0.0  0.7   0:02.07 java

    步骤三:转换线程ID

    printf "%x
    " 17880          
    45d8

    步骤四:定位cpu占用线程

    jstack 17850|grep 45d8 -A 30
    "pool-1-thread-11" #20 prio=5 os_prio=0 tid=0x00007fc860352800 nid=0x45d8 runnable [0x00007fc8417d2000]
       java.lang.Thread.State: RUNNABLE
            at java.io.FileOutputStream.writeBytes(Native Method)
            at java.io.FileOutputStream.write(FileOutputStream.java:326)
            at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
            at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
            - locked <0x00000006c6c2e708> (a java.io.BufferedOutputStream)
            at java.io.PrintStream.write(PrintStream.java:482)
            - locked <0x00000006c6c10178> (a java.io.PrintStream)
            at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
            at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)
            at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104)
            - locked <0x00000006c6c26620> (a java.io.OutputStreamWriter)
            at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)
            at java.io.PrintStream.write(PrintStream.java:527)
            - eliminated <0x00000006c6c10178> (a java.io.PrintStream)
            at java.io.PrintStream.print(PrintStream.java:597)
            at java.io.PrintStream.println(PrintStream.java:736)
            - locked <0x00000006c6c10178> (a java.io.PrintStream)
            at com.demo.guava.HardTask.call(HardTask.java:18)
            at com.demo.guava.HardTask.call(HardTask.java:9)
            at java.util.concurrent.FutureTask.run(FutureTask.java:266)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
            at java.lang.Thread.run(Thread.java:745)
    
    "pool-1-thread-10" #19 prio=5 os_prio=0 tid=0x00007fc860345000 nid=0x45d7 waiting on condition [0x00007fc8418d3000]
       java.lang.Thread.State: WAITING (parking)
            at sun.misc.Unsafe.park(Native Method)
            - parking to wait for  <0x00000006c6c14178> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
            at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)

    五.总结

    遇到线上问题不要慌,首先确认排查问题的思路:

    (1)查看日志;

    (2)查看CPU情况;

    (3)查看TCP情况;

    (4)查看java线程,jstack;

    (5)查看java堆,jmap;

    (6)通过MAT分析堆文件,寻找无法被回收的对象。

  • 相关阅读:
    关于dllimport的使用
    公众平台返回原始数据为: 错误代码-40164
    CentOS7.4 系统下 Tomcat 启动慢解决方法
    PyCharm实现高效远程调试代码
    代码比较工具推荐
    CentOS7 下源码安装 python3
    linux定时任务调度定系统——opencron
    使用 ISO镜像配置 本地yum 源(RHEL, CentOS, Fedora等适用)
    Error: rpmdb open failed
    部署Redis(脚本安装)
  • 原文地址:https://www.cnblogs.com/ZJOE80/p/12938421.html
Copyright © 2020-2023  润新知