1. JDK命令行工具

JDK命令行工具主要用于JVM性能监控和故障处理。

1.1 jps

虚拟机进程状况工具。 显示当前所有java进程pid的命令。

-l 输出主类的全名,如果进程执行的是Jar包,输出Jar路径

-v 输出虚拟机进程启动时JVM参数

-q 只输出LVMID,省略主类的名称

-m 输出虚拟机进程启动时传递给主类main()函数的参数

1.2 jstat

虚拟机统计信息监视工具,用于监视虚拟机各种运行状态信息的命令。可以显示本地或远程虚拟机进程中的类加载、内存、垃圾回收、JIT编译等运行数据。运行期定位虚拟机性能问题的首选工具。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

-class:监视类装载、卸载数量、总空间以及类装载所耗时间。

-gc:监视JAVA堆状况,包括Eden区、两个Survivor区、老年代、永久代等的容量、已用空间,GC已用时间合计等信息;

-gccapacity:监视内容与-gc基本相同,但输出主要关注 java堆各个区域使用到的最大、最小空间;

-gcutil:监视内容与-gc基本相同,便输出主要关注已使用空间占总空间的百分比;

-gccause:与-gcutil功能一样,但是会额外输出导致 一次GC产生的原因;

-gcnew:监视新生代GC状态;

-gcnewcapacity:监视内容与-gcnew基本相同,输出最要关注使用到的最大、最小空间;

-gcold:监视老年代GC状况;

-gcoldcapacity:监视内容与-gcold基本相同,输出主要关注使用到的最大、最小空间;

-gcpermcapacity:监视永久代使用到的最大、最小空间;

-compiler:输入JIT编译器编译过的方法,耗时等信息;

-printcompilation:输出已经被JIT编译的方法;

1.3 jinfo

1
2
3
4
5
6
7

-flag <name> :可查看虚拟机启动时显式指定的参数列表。
-flag [+|-]<name>:设置或取消VM参数
-flag <name>=<value>:给VM参数设置新值
-flags:可查看所有VM参数;
-sysprops:查看java系统参数;
<no option>:表示在不给定任何选项时,打印出以上所有的VM参数

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

1.4 jmap

1
2
3
4
5
6
7
8
9
10
11
12

-dump:生成java堆转储快照,格式为:-dump[live, ] format=b, file=<filename>,其中live子参数说明是否只dump出存活对象;

-finalizerinfo:显示在F-QueuiK 等待Finalizer线程执行finalize方法的对象。只在Linux/Solaris平台下有效;

-heap:显示java堆详细信息,如使用哪种回收器、参数配置、分代状况等。只在Linux/Solaris平台下有效;

-histo:显示堆中对象统计信息,包括类、实例数量、合计容量;

-permstat:以ClassLoader为统计口径显示永久代内存状态,只在Linux/Solaris平台有效;

-F:当虚拟机进程对-dump选项没有响应时,可使用这个选项强制生成dump快照,只在Linux/Solaris平台有效

Java内存映像工具。用于生成堆转储快照,还可以查询finalize()执行队列,Java堆和永久代的详细信息,如空间使用率、当前使用的哪种收集器等。

1
2
3
C:\Program Files\Java\jdk1.8.0_161\bin>jmap -dump:format=b,file=D:\test.bin 2768
Dumping heap to D:\test.bin ...
Heap dump file created

1.5 jhat

jhat D:\test.bin

分析jmap生成的堆转储快照,在浏览器查看。

1.6 jstack

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

-F 当正常输出的请求不被响应时,强制输出线程堆栈

-l 除堆栈外,显示关于锁的附加信息

-m 如果调用到本地方法的话,可以显示c/c++的堆栈

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
56

public class Test {

public static void main(String[] args){

// Thread t1 = new Worker(1);
//
//
// t1.start();
final A a = new A();
final B b = new B();

new Thread(){
@Override
public void run(){
try {
sleep(3000);
} catch (InterruptedException e) {

}
a.getBLock(b);
}
}.start();

new Thread(){
@Override
public void run(){
try {
sleep(3000);
} catch (InterruptedException e) {

}
b.getALock(a);
}
}.start();

}

}


class A {

public synchronized void getBLock(B b){

b.getALock(this);
}

}

class B{

public synchronized void getALock(A a){
a.getBLock(this);
}
}

上述代码会产生死锁,使用jstack命令可以打印出线程死锁信息。

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

C:\Program Files\Java\jdk1.8.0_161\bin>jps -m
1904 AppMain com.zhongyp.Test
944 RemoteMavenServer
11540 Jps -m
13204 Launcher F:/Program Files/intellij IDEA/lib/log4j.jar;F:/Program Files/intellij IDEA/lib/jps-builders.jar;F:/Program Files/intellij IDEA/lib/netty-all-4.1.1.Final.jar;F:/Program Files/intellij IDEA/lib/annotations.jar;F:/Program Files/intellij IDEA/lib/rt/jps-plugin-system.jar;F:/Program Files/inte
llij IDEA/lib/jgoodies-forms.jar;F:/Program Files/intellij IDEA/lib/util.jar;F:/Program Files/intellij IDEA/lib/trove4j.jar;F:/Program Files/intellij IDEA/lib/jna.jar;F:/Program Files/intellij IDEA/lib/resources_en.jar;F:/Program Files/intellij IDEA/lib/oromatcher.jar;F:/Program Files/intellij IDEA/lib/i
dea_rt.jar;F:/Program Files/intellij IDEA/lib/openapi.jar;F:/Program Files/intellij IDEA/lib/javac2.jar;F:/Program Files/intellij IDEA/lib/snappy-in-java-0.5.1.jar;F:/Program Files/intellij IDEA/lib/jna-platform.jar;F:/Program Files/intellij IDEA/lib/forms_rt.jar;F:/Program Files/intellij IDEA/lib/jdom.j
ar;F:/Program Files/intellij IDEA/lib/asm-all.jar;F:/Program Files/intellij IDEA/lib/jps-
4488



C:\Program Files\Java\jdk1.8.0_161\bin>jstack -F 1904
Attaching to process ID 1904, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.161-b12
Deadlock Detection:

Found one Java-level deadlock:
=============================

"Thread-0":
waiting to lock Monitor@0x00000000193ce108 (Object@0x00000000d6129700, a com/zhongyp/B),
which is held by "Thread-1"
"Thread-1":
waiting to lock Monitor@0x00000000193cb878 (Object@0x00000000d6127d20, a com/zhongyp/A),
which is held by "Thread-0"

Found a total of 1 deadlock.

Thread 1: (state = BLOCKED)


Thread 18: (state = BLOCKED)
- com.zhongyp.A.getBLock(com.zhongyp.B) @bci=0, line=51 (Interpreted frame)
- com.zhongyp.B.getALock(com.zhongyp.A) @bci=2, line=59 (Interpreted frame)
- com.zhongyp.Test$2.run() @bci=18, line=38 (Interpreted frame)


Thread 17: (state = BLOCKED)
- com.zhongyp.B.getALock(com.zhongyp.A) @bci=0, line=59 (Interpreted frame)
- com.zhongyp.A.getBLock(com.zhongyp.B) @bci=2, line=51 (Interpreted frame)
- com.zhongyp.Test$1.run() @bci=18, line=26 (Interpreted frame)


Thread 16: (state = IN_NATIVE)
- java.net.DualStackPlainSocketImpl.accept0(int, java.net.InetSocketAddress[]) @bci=0 (Interpreted frame)
- java.net.DualStackPlainSocketImpl.socketAccept(java.net.SocketImpl) @bci=37, line=131 (Interpreted frame)
- java.net.AbstractPlainSocketImpl.accept(java.net.SocketImpl) @bci=7, line=409 (Interpreted frame)
- java.net.PlainSocketImpl.accept(java.net.SocketImpl) @bci=42, line=199 (Interpreted frame)
- java.net.ServerSocket.implAccept(java.net.Socket) @bci=60, line=545 (Interpreted frame)
- java.net.ServerSocket.accept() @bci=48, line=513 (Interpreted frame)
- com.intellij.rt.execution.application.AppMain$1.run() @bci=13, line=79 (Interpreted frame)
- java.lang.Thread.run() @bci=11, line=748 (Interpreted frame)


Thread 10: (state = BLOCKED)


Thread 9: (state = BLOCKED)


Thread 8: (state = BLOCKED)
- java.lang.Object.wait(long) @bci=0 (Interpreted frame)
- java.lang.ref.ReferenceQueue.remove(long) @bci=59, line=143 (Interpreted frame)
- java.lang.ref.ReferenceQueue.remove() @bci=2, line=164 (Interpreted frame)
- java.lang.ref.Finalizer$FinalizerThread.run() @bci=36, line=209 (Interpreted frame)


Thread 7: (state = BLOCKED)
- java.lang.Object.wait(long) @bci=0 (Interpreted frame)
- java.lang.Object.wait() @bci=2, line=502 (Interpreted frame)
- java.lang.ref.Reference.tryHandlePending(boolean) @bci=54, line=191 (Interpreted frame)
- java.lang.ref.Reference$ReferenceHandler.run() @bci=1, line=153 (Interpreted frame)

2. JDK可视化工具

JConsole: Java监视与管理控制台。基于JMX的可视化监视和管理工具。监视Java堆和永久代的的变化趋势。

VisualVM: 多合一故障处理工具。

2.1 MAT: 堆dump文件分析工具

2.1.1 Histogram可以列出内存中的对象,对象的个数以及大

Class Name:类名称,java类名

Objects:类的对象的数量,这个对象被创建了多少个

Shallow Heap:一个对象内存的消耗大小,不包含对其他对象的引用,Shallow Heap堆中的对象是它的大小和保留内存大小相同的对象是堆内存的数量时,将释放对象被垃圾收集。
对象自身占用的内存大小,不包括它引用的对象。
针对非数组类型的对象,它的大小就是对象与它所有的成员变量大小的总和。当然这里面还会包括一些java语言特性的数据存储单元。
针对数组类型的对象,它的大小是数组元素对象的大小总和。

Retained Heap:是shallow Heap的总和,也就是该对象被GC之后所能回收到内存的总和。
Retained Size=当前对象大小+当前对象可直接或间接引用到的对象的大小总和。(间接引用的含义:A->B->C, C就是间接引用)
换句话说,Retained Size就是当前对象被GC后,从Heap上总共能释放掉的内存。
不过,释放的时候还要排除被GC Roots直接或间接引用的对象。他们暂时不会被被当做Garbage。

2.1.2 Dominator Tree可以列出那个线程,以及线程下面的那些对象占用的空间

2.1.3 Top consumers通过图形列出最大的object

2.1.4 Leak Suspects通过MA自动分析泄漏的原因

3. 内存问题分析

JVM故障分析系列

Memory Analyzer Tool 使用手记

使用Eclipse Memory Analyzer分析内存

使用Memory Analyzer tool(MAT)分析内存泄漏(一)

Shallow and retained sizes

JVM内存回收理论与实现

GC roots

一次使用Eclipse Memory Analyzer分析Tomcat内存溢出

参考资料

Shallow heap & Retained heap