Java开发必会Java指令

Java 常用命令

在现实企业级Java应用开发中,有时候我们会碰到下面这些问题:

  • OutOfMemoryError,内存不足

  • 内存泄露

  • 线程死锁

  • 锁争用(Lock Contention)

  • Java进程消耗CPU过高

  • ......

    这些问题在日常开发中可能被很多人忽视(比如有的人遇到上面的问题只是重启服务器或者调大内存,而不会深究问题根源),但能够理解并解决这些问题是Java程序员进阶的必备要求。本文将对一些常用的JVM性能调优监控工具进行介绍:

  • java

  • javac

  • jps

  • jstat

  • jmap

  • jstack

指令详解

jps(Java Virtual Machine Process Status Tool)

jps主要用来输出JVM中运行的进程状态信息。如果不指定hostid就默认为当前主机或服务器。语法格式如下:

jps [options] [hostid]

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

-q 不输出类名、Jar名和传入main方法的参数
-m 输出传入main方法的参数
-l 输出main类或Jar的全限名
-v 输出传入JVM的参数

比如下面:

root@ubuntu:/# jps -m -l
2458 org.artifactory.standalone.main.Main /usr/local/artifactory-2.2.5/etc/jetty.xml
29920 com.sun.tools.hat.Main -port 9998 /tmp/dump.dat
3149 org.apache.catalina.startup.Bootstrap start
30972 sun.tools.jps.Jps -m -l
8247 org.apache.catalina.startup.Bootstrap start
25687 com.sun.tools.hat.Main -port 9999 dump.dat
21711 mrf-center.jar

官方jps解释

jastck

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可以定位到线程堆栈,根据堆栈信息我们可以定位到具体代码,所以它在JVM性能调优中使用得非常多。

下面演示两个使用jstack排查死锁与死循环的命令。

Demo1:死锁问题排查

人为地编写如下的死锁测试代码:

public class DeadLock extends Thread{
    private String first;
    private String second;

    public DeadLock(String name, String first, String second){
        super(name);
        this.first=first;
        this.second=second;
    }

    public void run(){
        synchronized (first){
           System.out.println(Thread.currentThread().getName()+"obtained: "+first);
           try {
               Thread.sleep(1000L);
               synchronized (second){
                   System.out.println(Thread.currentThread().getName()+"obtained: "+second);
               }
           }catch (InterruptedException e){
               e.printStackTrace();
           }
        }
    }

    public static void main(String[] args) throws InterruptedException{
        String lockA="lockA";
        String lockB="lockB";
        DeadLock t1=new DeadLock("Thread1",lockA,lockB);
        DeadLock t2=new DeadLock("Thread2",lockB,lockA);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}
vim DeadLock.java 
javac DeadLock.java 
java DeadLock

输出显示如下:

Thread1obtained: lockA
Thread2obtained: lockB

shell窗口光标一直闪烁,没有运行结束,程序进入死锁状态。
此时,我们重新打开一个新的窗口,运行jps找到DeadLock的Java进程号。

root@iZbp11s2rh7xf6u48hlcroZ:~# jps -ml
16977 org.apache.catalina.startup.Bootstrap start
22134 DeadLock
22231 sun.tools.jps.Jps -ml
4350 com.aliyun.tianji.cloudmonitor.Application

接下来执行:jstack 22134

Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.181-b13 mixed mode):

"Attach Listener" #10 daemon prio=9 os_prio=0 tid=0x00007fb6e0001000 nid=0x56c8 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread2" #9 prio=5 os_prio=0 tid=0x00007fb7080dc800 nid=0x5681 waiting for monitor entry [0x00007fb6f361f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at DeadLock.run(DeadLock.java:17)
        - waiting to lock <0x00000000e0a5dcf0> (a java.lang.String)
        - locked <0x00000000e0a5dd28> (a java.lang.String)

"Thread1" #8 prio=5 os_prio=0 tid=0x00007fb7080da800 nid=0x5680 waiting for monitor entry [0x00007fb6f3720000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at DeadLock.run(DeadLock.java:17)
        - waiting to lock <0x00000000e0a5dd28> (a java.lang.String)
        - locked <0x00000000e0a5dcf0> (a java.lang.String)


Found one Java-level deadlock:
=============================
"Thread2":
  waiting to lock monitor 0x00007fb6d8005848 (object 0x00000000e0a5dcf0, a java.lang.String),
  which is held by "Thread1"
"Thread1":
  waiting to lock monitor 0x00007fb6ec0062c8 (object 0x00000000e0a5dd28, a java.lang.String),
  which is held by "Thread2"

Java stack information for the threads listed above:
===================================================
"Thread2":
        at DeadLock.run(DeadLock.java:17)
        - waiting to lock <0x00000000e0a5dcf0> (a java.lang.String)
        - locked <0x00000000e0a5dd28> (a java.lang.String)
"Thread1":
        at DeadLock.run(DeadLock.java:17)
        - waiting to lock <0x00000000e0a5dd28> (a java.lang.String)
        - locked <0x00000000e0a5dcf0> (a java.lang.String)

Found 1 deadlock.

线程运行的堆栈信息以及死锁检测的信息均帮助我们打印出来了。

Demo2 CPU飙升排查(死循环排查)

第一步先找出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飙升,这个时候就需要使用到TOP命令来进行查找。死循环会导致CPU不断的飙升,这时候使用top命令,查看cpu占用率较高的命令:


426671-ad81e7454e703b5c.png

第二步找出该进程内最耗费CPU的线程,可以使用top -Hp pid,我这里用第三个,输出如下:

170402_A57i_111708.png

TIME列就是各个Java线程耗费的CPU时间,CPU时间最长的是线程ID为21742的线程,用

printf "%x\n" 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) {
    }
}

jinfo

jinfo用于查看JVM启动时候设置的参数值
jinfo可以查看当前JVM线程配置的系统属性,以及运行时设置的参数值。

直接使用jinfo

前半段是系统的属性

root@iZbp11s2rh7xf6u48hlcroZ:~# jinfo 22134
Attaching to process ID 22134, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.181-b13
Java System Properties:

java.runtime.name = Java(TM) SE Runtime Environment
java.vm.version = 25.181-b13
sun.boot.library.path = /usr/lib/jvm/java-8-oracle/jre/lib/amd64
java.vendor.url = http://java.oracle.com/
java.vm.vendor = Oracle Corporation
path.separator = :
file.encoding.pkg = sun.io
java.vm.name = Java HotSpot(TM) 64-Bit Server VM
sun.os.patch.level = unknown
sun.java.launcher = SUN_STANDARD
user.country = US
user.dir = /home/louxj424/document
java.vm.specification.name = Java Virtual Machine Specification
java.runtime.version = 1.8.0_181-b13
java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment
os.arch = amd64
java.endorsed.dirs = /usr/lib/jvm/java-8-oracle/jre/lib/endorsed
java.io.tmpdir = /tmp
line.separator = 

后半段是jvm的参数

VM Flags:
Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=33554432 -XX:MaxHeapSize=526385152 -XX:MaxNewSize=175439872 -XX:MinHeapDeltaBytes=196608 -XX:NewSize=11141120 -XX:OldSize=22413312 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops 
Command line:  

我们也可以直接使用jinfo查看具体的某个参数值:

root@iZbp11s2rh7xf6u48hlcroZ:~# jinfo -flag MaxHeapSize 22134
-XX:MaxHeapSize=526385152

jstat

显示JVM的性能统计信息,
常见用法:jstat -

查看JIT编译信息

root@iZbp11s2rh7xf6u48hlcroZ:~# jstat -compiler -t 22478 1 5
Timestamp       Compiled Failed Invalid   Time   FailedType FailedMethod
          370.3       11      0       0     0.00          0             
          370.3       11      0       0     0.00          0             
          370.3       11      0       0     0.00          0             
          370.3       11      0       0     0.00          0             
          370.3       11      0       0     0.00          0 

查看GC信息

root@iZbp11s2rh7xf6u48hlcroZ:~# jstat -gc -t 22478
Timestamp        S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
           78.8 1088.0 1088.0  0.0    0.0    8704.0   870.7    21888.0      0.0     4480.0 779.2  384.0   76.4       0    0.000   0      0.000    0.000
root@iZbp11s2rh7xf6u48hlcroZ:~# jstat -gc -t 22478 1 5
Timestamp        S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
           83.0 1088.0 1088.0  0.0    0.0    8704.0   870.7    21888.0      0.0     4480.0 779.2  384.0   76.4       0    0.000   0      0.000    0.000
           83.0 1088.0 1088.0  0.0    0.0    8704.0   870.7    21888.0      0.0     4480.0 779.2  384.0   76.4       0    0.000   0      0.000    0.000
           83.0 1088.0 1088.0  0.0    0.0    8704.0   870.7    21888.0      0.0     4480.0 779.2  384.0   76.4       0    0.000   0      0.000    0.000
           83.0 1088.0 1088.0  0.0    0.0    8704.0   870.7    21888.0      0.0     4480.0 779.2  384.0   76.4       0    0.000   0      0.000    0.000
           83.0 1088.0 1088.0  0.0    0.0    8704.0   870.7    21888.0      0.0     4480.0 779.2  384.0   76.4       0    0.000   0      0.000    0.000

说明:

  1. -gc表示查看垃圾收集器中的信息, 主要包含jvm的运行时数据区统计。
  2. -t TID指定要查看的JVM进程号。
  3. 1表示每秒扫描一次,5表示执行5次。如果不指定则1秒执行一次,执行一次。

下面针对输出结果做一下解释:

JVM内存布局.jpg

堆内存 = 年轻代 + 年老代 + 永久代
年轻代 = Eden区 + 两个Survivor区(From和To)

后缀为C的代表当前区的容量,后缀为U的代表已经使用了多少容量,后缀为T的代表耗时

S0C 存活区0的容量(KB)
S1C 存活区1的容量(KB)
S0U 存活区0使用的空间 (KB).
S1U 存活区1的利用空间 (KB).
EC Eden区的容量(KB).
EU Eden区利用的容量(KB).
OC 老年代容量(KB).
OU 老年代使用容量(KB).
PC 当前永久带的容量(KB).
PU 永久带使用容量(KB).
YGC 发生了多少次Young GC
YGCT Young GC的时间
FGC Full GC的次数
FGCT Full GC的收集时间
GCT 总共的GC时间.

查看JVM中的class信息。

root@iZbp11s2rh7xf6u48hlcroZ:~# jstat -class -t 22478 1 5        
Timestamp       Loaded  Bytes  Unloaded  Bytes     Time   
          433.5    389   811.1        0     0.0       0.04
          433.5    389   811.1        0     0.0       0.04
          433.5    389   811.1        0     0.0       0.04
          433.5    389   811.1        0     0.0       0.04
          433.5    389   811.1        0     0.0       0.04

jmap

jmap常用于分析堆。在发生OME(OutOfMemoryError)的时候,会用jmap分析堆中具体是什么问题,才能更好的解决问题。jmap一般和mat配合使用。

一般在java开发的项目启动时候,最好加上下面命令,在内存溢出的时候可以通过日志查看信息。

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/java/dump

当然在项目运行的时候也可以使用jmap -heap jvmpid查看对象内存的映射。

演示内存溢出
堆内存溢出代码

import java.util.LinkedList;
import java.util.List;

public class DealCycle {
    public static List list = new LinkedList<>();
    public static void main(String[] args) {
        int i = 0;
        while (true){
            list.add(i);
        }
    }
}

运行:

root@iZbp11s2rh7xf6u48hlcroZ:/home/louxj424/document# java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./ -Xms10m -Xmx10m DealCycle 
java.lang.OutOfMemoryError: Java heap space
Dumping heap to ./java_pid22806.hprof ...
Heap dump file created [21024262 bytes in 0.105 secs]

Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"

由于在运行时指定的dump文件的输出位置为当前目录下,我们查看下当前目录发现多了一个文件:java_pid22806.hprof

在线分析hprof文件:
http://heaphero.io/

当然在项目运行的时候也可以使用jmap -heap jvmpid查看对象内存的映射。如果每次都等到内存溢出才导出文件时间就有些晚了,可以使用jmap直接导出。

root@iZbp11s2rh7xf6u48hlcroZ:/home/louxj424/document# jmap -dump:file=./dumpFile 22478
Dumping heap to /home/louxj424/document/dumpFile ...
Heap dump file created

查看下当前目录,发现多了一个dumpFile的文件,该文件就是我们在程序运行时dump出来的内存映射文件,可以借助其他的工具做进一步的分析。

使用jmap -heap pid查看进程堆内存使用情况,包括使用的GC算法、堆配置参数和各代中堆内存使用情况:

root@iZbp11s2rh7xf6u48hlcroZ:/home/louxj424/document# jmap -heap 22478
Attaching to process ID 22478, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.181-b13

using thread-local object allocation.
Mark Sweep Compact GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 526385152 (502.0MB)
   NewSize                  = 11141120 (10.625MB)
   MaxNewSize               = 175439872 (167.3125MB)
   OldSize                  = 22413312 (21.375MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 10027008 (9.5625MB)
   used     = 1070208 (1.0206298828125MB)
   free     = 8956800 (8.5418701171875MB)
   10.673253676470589% used
Eden Space:
   capacity = 8912896 (8.5MB)
   used     = 1070208 (1.0206298828125MB)
   free     = 7842688 (7.4793701171875MB)
   12.007410386029411% used
From Space:
   capacity = 1114112 (1.0625MB)
   used     = 0 (0.0MB)
   free     = 1114112 (1.0625MB)
   0.0% used
To Space:
   capacity = 1114112 (1.0625MB)
   used     = 0 (0.0MB)
   free     = 1114112 (1.0625MB)
   0.0% used
tenured generation:
   capacity = 22413312 (21.375MB)
   used     = 0 (0.0MB)
   free     = 22413312 (21.375MB)
   0.0% used

735 interned Strings occupying 49592 bytes.

参考资料

  1. JVM性能调优监控工具jps、jstack、jmap、jhat、jstat、hprof使用详解
  2. Java命令行监控工具(jmap,jstack,jstat,jinfo,jps)

你可能感兴趣的:(Java开发必会Java指令)