前言

本文我们来学习下JVM中的一些常用的指令。

jps

介绍

jps 是(java process Status Tool), Java版的ps命令,查看java进程及其相关的信息,如果你想找到一个java进程的pid,那可以用jps命令替代linux中的ps命令了,简单而方便。

命令格式:

jps [options] [hostid]

options参数解释:

  • -l : 显示进程id,显示主类全名或jar路径
  • -q : 显示进程id
  • -m : 显示进程id, 显示JVM启动时传递给main()的参数
  • -v : 显示进程id,显示JVM启动时显示指定的JVM参数

hostid : 主机或其他服务器ip

最常用示例:

1
2
3
jps -l 输出jar包路径,类全名
jps -m 输出main参数
jps -v 输出JVM参数

实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.example.test;

/**
* jps -q 显示进程id
* jps -l 输出jar包路径,类全名
* jps -m 输出主类名,及传入main方法的参数
* jps -v 输出主类名,及输出JVM参数
*/
public class Demo_jps {
public static void main(String[] args) {
System.out.println("jps指令");
try {
Thread.sleep(200000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

jps -l命令 :

jps -v命令

先设置堆的空间大小:

执行:

jps -m命令

先设置main方法参数:

执行:

jinfo

介绍

jinfo是用来查看JVM参数和动态修改部分JVM参数的命令

命令格式:

jinfo [option]

其中pid是进程号,由jps指令查看得到。

options参数解释:

  • no options 输出所有的系统属性和参数
  • -flag 打印指定名称的参数
  • -flag [+|-] 打开或关闭参数
  • -flag = 设置参数
  • -flags 打印所有参数
  • -sysprops 打印系统配置

实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.example.test;

/**
jinfo [option] <pid>
   options参数解释:
- no options 输出所有的系统属性和参数
- -flag <name> 打印指定名称的参数
- -flag [+|-]<name> 打开或关闭参数
- -flag <name>=<value> 设置参数
- -flags 打印所有参数
- -sysprops 打印系统配置
*
*/
public class Demo_jinfo {
public static void main(String[] args) {
System.out.println("jinfo指令");
try {
Thread.sleep(200000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

通过jps查看得到进程号为11666。

查看JVM参数和系统配置:

1
2
3
jinfo 11666
jinfo -flags 11666
jinfo -sysprops 11666

查看打印GC日志参数:

1
2
jinfo -flag PrintGC 11666
jinfo -flag PrintGCDetails 11666

打开GC日志参数:

1
2
jinfo -flag +PrintGC 11666
jinfo -flag +PrintGCDetails 11666

关闭GC日志参数:

1
2
jinfo -flag -PrintGC 11666
jinfo -flag -PrintGCDetails 11666

常用JVM参数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-Xms:初始堆大小,默认为物理内存的1/64(<1GB);默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制 

-Xmx:最大堆大小,默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制

-Xmn:新生代的内存空间大小,注意:此处的大小是(eden+ 2 survivor space)。与jmap -heap中显示的New gen是不同的。整个堆大小=新生代大小 + 老生代大小 + 永久代大小。

在保证堆大小不变的情况下,增大新生代后,将会减小老生代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。

-XX:SurvivorRatio:新生代中Eden区域与Survivor区域的容量比值,默认值为8。两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10。

-Xss:每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。应根据应用的线程所需内存大小进行适当调整。在相同物理内存下, 减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。一般小的应用, 如果栈不是很深, 应该是128k够用的,大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。和threadstacksize选项解释很类似,官方文档似乎没有解释, 在论坛中有这样一句话:"-Xss `ìs translated `ìn a VM flag named ThreadStackSize”一般设置这个值就可以了。

-XX:PermSize:设置永久代(perm gen)初始值。默认值为物理内存的1/64。

-XX:MaxPermSize:设置持久代最大值。物理内存的1/4。

jstat

介绍

jstat命令是使用频率比较高的命令,主要用来查看JVM运行时的状态信息,包括内存状态、垃圾回收等。

命令格式:

jstat [option] VMID [interval] [count ]

其中VMID是进程id,interval是打印间隔时间(毫秒),count是打印次数(默认一直打印)

option参数解释:

  • -class class loader的行为统计
  • -compiler HotSpt JIT编译器行为统计
  • -gc 垃圾回收堆的行为统计
  • -gccapacity 各个垃圾回收代容量(young,old,perm)和他们相应的空间统计
  • -gcutil 垃圾回收统计概述
  • -gccause 垃圾收集统计概述(同-gcutil),附加最近两次垃圾回收事件的原因
  • -gcnew 新生代行为统计
  • -gcnewcapacity 新生代与其相应的内存空间的统计
  • -gcold 年老代和永生代行为统计
  • -gcoldcapacity 年老代行为统计
  • -printcompilation HotSpot编译方法统计

实战

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.example.test;


public class Demo_jstat {
public static void main(String[] args) {
System.out.println("jstat指令");
try {
Thread.sleep(200000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

-gc查看jvm各个区间的容量使用情况:

字段解释:

S0C survivor0大小 S1C survivor1大小 S0U survivor0已使用大小 S1U survivor1已使用大小 EC Eden区大小 EU Eden区已使用大小 OC 老年代大小 OU 老年代已使用大小 MC 方法区大小 MU 方法区已使用大小 CCSC 压缩类空间大小 CCSU 压缩类空间已使用大小 YGC 年轻代垃圾回收次数 YGCT 年轻代垃圾回收消耗时间 FGC Full GC垃圾回收次数 FGCT Full GC垃圾回收消耗时间 GCT 垃圾回收消耗总时间

-gcutil表示的是各个区间的容量使用占比情况:

各个字段的值的含义如下:

S0 survivor0使用百分比 S1 survivor1使用百分比 E Eden区使用百分比 O 老年代使用百分比 M 元数据区使用百分比 CCS 压缩使用百分比 YGC 年轻代垃圾回收次数 YGCT 年轻代垃圾回收消耗时间 FGC Full GC垃圾回收次数 FGCT Full GC垃圾回收消耗时间 GCT 垃圾回收消耗总时间

-gc和-gcutil的区别:-gcutil表示的是占比情况,-gc表示的是实际的值。

jstack

介绍

jstack是用来查看JVM线程快照的命令,线程快照是当前JVM线程正在执行的方法堆栈集合。使用jstack命令可以定 位线程出现长时间卡顿的原因,例如死锁,死循环等。jstack还可以查看程序崩溃时生成的core文件中的stack信 息。

命令格式:

jstack [options]

option参数解释:

-F 当使用jstack 无响应时,强制输出线程堆栈; -m 同时输出java堆栈和c/c++堆栈信息(混合模式); -l 除了输出堆栈信息外,还显示关于锁的附加信息。

CPU使用率过高问题

1.使用Process Explorer工具找到cpu占用率较高的进程 2.在thread卡中找到cpu占用高的线程id 3.线程id转换成16进制 4.使用jstack -l 查看进程的线程快照 5.线程快照中找到指定线程,并分析代码

实战案例

1
2
3
4
5
6
public class Demo_jstack {
public static void main(String[] args) {
while (true){
}
}
}

jps查看到对应的进程id是21788:

使用Process Explorer工具找到cpu占用率较高的进程:

在thread卡中找到cpu占用高的线程id,并将线程id转换成16进制:

使用jstack -l 查看进程的所有线程快照:

线程快照中找到指定线程,并分析代码:

死锁问题

实战案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.example.test;

public class DeadLock {
private static Object obj1 = new Object();
private static Object obj2 = new Object();

public static void main(String[] args) {
new Thread(new Thread1()).start();
new Thread(new Thread2()).start();
}

private static class Thread1 implements Runnable{

@Override
public void run() {
synchronized (obj1){

System.out.println("Thread1 拿到了obj1的锁");

//休眠2秒是为了让线程2拿到obj2的锁
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}

synchronized (obj2){
System.out.println("Thread1 拿到了obj2的锁");
}
}
}
}

private static class Thread2 implements Runnable{

@Override
public void run() {
synchronized (obj2){

System.out.println("Thread2 拿到了obj2的锁");

try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}

synchronized (obj1){
System.out.println("Thread2 拿到了obj1的锁");
}
}
}
}

}

说明:上面是一段死锁的代码。两个线程分别持有对方需要的锁。

通过jstack -l指令来排查死锁:

能看到对应的死锁信息及对应死锁的代码行:

然后我们定位对应的死锁行,对代码逻辑进行修改即可。

jmap

介绍

jmap可以生成 java 程序的 dump 文件, 也可以查看堆内对象示例的统计信息、查看 ClassLoader 的信息以及 finalizer 队列。

jmap [option] (连接正在执行的进程)

如果使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每个共享对象的起始 地址、 映射大小以及共享对象文件的路径全称。

option参数解释:

-heap 打印java heap摘要 -histo[:live] 打印堆中的java对象统计信息 -clstats 打印类加载器统计信息 -finalizerinfo 打印在f-queue中等待执行finalizer方法的对象 -dump: 生成java堆的dump文件 dump-options: live 只转储存活的对象,如果没有指定则转储所有对象 format=b 二进制格式 file= 转储文件到

实战案例

对于结果中的信息进行如下说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Heap Configuration: //堆内存初始化配置
  MinHeapFreeRatio  = 0 //对应jvm启动参数-XX:MinHeapFreeRatio设置JVM堆最小空闲比率(default 40)
  MaxHeapFreeRatio  = 100 //对应jvm启动参数 -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率(default 70)
  MaxHeapSize= 1073741824 (1024.0MB) //对应jvm启动参数-XX:MaxHeapSize=设置JVM堆的最大大小
  NewSize    = 22020096 (21.0MB) //对应jvm启动参数-XX:NewSize=设置JVM堆的新生代的默认大小
  MaxNewSize = 357564416 (341.0MB) //对应jvm启动参数-XX:MaxNewSize=设置JVM堆的新生代的最大大小
  OldSize    = 45088768 (43.0MB) //对应jvm启动参数-XX:OldSize=<value>:设置JVM堆的老年代的大小
  NewRatio   = 2 //对应jvm启动参数-XX:NewRatio=:新生代和老生代的大小比率
  SurvivorRatio     = 8 //对应jvm启动参数-XX:SurvivorRatio=设置新生代中Eden区与Survivor区的大小比值
  MetaspaceSize     = 21807104 (20.796875MB) // 元数据区大小
  CompressedClassSpaceSize = 1073741824 (1024.0MB) //类压缩空间大小
  MaxMetaspaceSize  = 17592186044415 MB //元数据区最大大小
  G1HeapRegionSize  = 0 (0.0MB) //G1垃圾收集器每个Region大小

Heap Usage: //堆内存使用情况
PS Young Generation
Eden Space: //Eden区内存分布
  capacity = 17825792 (17.0MB) //Eden区总容量
  used     = 12704088 (12.115562438964844MB) //Eden区已使用
  free     = 5121704 (4.884437561035156MB) //Eden区剩余容量
  71.26801434685203% used //Eden区使用比率


From Space: //其中一个Survivor区的内存分布
  capacity = 2097152 (2.0MB)
  used     = 1703936 (1.625MB)
  free     = 393216 (0.375MB)
  81.25% used
To Space: //另一个Survivor区的内存分布
  capacity = 2097152 (2.0MB)
  used     = 0 (0.0MB)
  free     = 2097152 (2.0MB)
  0.0% used
PS Old Generation
  capacity = 52428800 (50.0MB) //老年代容量
  used     = 28325712 (27.013504028320312MB) //老年代已使用
  free     = 24103088 (22.986495971679688MB) //老年代空闲
  54.027008056640625% used //老年代使用比率

15884 interned Strings occupying 2075304 bytes.

输出存活对象统计信息:

jmap -dump这个命令是要把java堆中的存活对象信息转储到dump.bin文件:

jhat

介绍

jhat是用来分析jmap生成dump文件的命令,jhat内置了应用服务器,可以通过网页查看dump文件分析结果,jhat一般是用在离线分析上。

jhat [option] [dumpfile]

option参数解释:

-stack false: 关闭对象分配调用堆栈的跟踪 -refs false: 关闭对象引用的跟踪 -port : HTTP服务器端口,默认是7000 -debug : debug级别 -version 分析报告版本

常用示例:

jhat dump.bin

实战案例

在浏览器访问localhost:7000:

里面有一些类的信息等等。我们可以进行查看。

jhat指令平时用的不是太多,我们了解下即可。

总结

我们学习了jps、jinfo、jstat、jstack、jmap、jhat这些常用的指令,并针对每个指令有对应的实战案例来做演示。