1、JDK命令行工具
1.1、jps命令
jps用于列出Java的进程,jps可以增加参数,-m用于输出传递给Java进程的参数,-l用于输出主函数的完整路径,-v可以用于显示传递给jvm的参数。
1
2
|
jps -l -m -v
31427
sun.tools.jps.Jps -l -m -v -Dapplication.home=/Library/Java/JavaVirtualMachines/jdk1.
7
.0_55.jdk/Contents/Home -Xms8m
|
1.2、jstat命令
jstat是一个可以用于观察Java应用程序运行时信息的工具,它的功能非常强大,可以通过它查看堆信息的详细情况,它的基本使用方法为:
1
|
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
|
选项option可以由以下值组成:
1
2
3
4
5
6
7
8
9
10
11
12
|
jstat -
class
pid:显示加载
class
的数量,及所占空间等信息。
jstat -compiler pid:显示VM实时编译的数量等信息。
jstat -gc pid:可以显示gc的信息,查看gc的次数,及时间。其中最后五项,分别是young gc的次数,young gc的时间,full gc的次数,full gc的时间,gc的总时间。
jstat -gccapacity:可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小,如:PGCMN显示的是最小perm的内存使用量,PGCMX显示的是perm的内存最大使用量,PGC是当前新生成的perm内存占用量,PC是但前perm内存占用量。其他的可以根据这个类推, OC是old内纯的占用量。
jstat -gcnew pid:
new
对象的信息。
jstat -gcnewcapacity pid:
new
对象的信息及其占用量。
jstat -gcold pid:old对象的信息。
jstat -gcoldcapacity pid:old对象的信息及其占用量。
jstat -gcpermcapacity pid: perm对象的信息及其占用量。
jstat -gcutil pid:统计gc信息统计。
jstat -printcompilation pid:当前VM执行的信息。
除了以上一个参数外,还可以同时加上 两个数字,如:jstat -printcompilation
3024
250
6
是每
250
毫秒打印一次,一共打印
6
次。
|
这些参数中最常用的参数是gcutil,下面是该参数的输出介绍以及一个简单例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
S0 — Heap上的 Survivor space
0
区已使用空间的百分比
S1 — Heap上的 Survivor space
1
区已使用空间的百分比
E — Heap上的 Eden space 区已使用空间的百分比
O — Heap上的 Old space 区已使用空间的百分比
P — Perm space 区已使用空间的百分比
YGC — 从应用程序启动到采样时发生 Young GC 的次数
YGCT– 从应用程序启动到采样时 Young GC 所用的时间(单位秒)
FGC — 从应用程序启动到采样时发生 Full GC 的次数
FGCT– 从应用程序启动到采样时 Full GC 所用的时间(单位秒)
GCT — 从应用程序启动到采样时用于垃圾回收的总时间(单位秒)
实例使用
1
:
[root
@localhost
bin]# jstat -gcutil
25444
S0 S1 E O P YGC YGCT FGC FGCT GCT
11.63
0.00
56.46
66.92
98.49
162
0.248
6
0.331
0.579
|
1.3、jinfo命令
jinfo可以用来查看正在运行的Java应用程序的扩展参数,甚至在运行时修改部分参数,它的基本语法为:
1
|
jinfo <option> <pid>
|
jinfo可以查看运行时参数:
1
2
|
jinfo -flag MaxTenuringThreshold
31518
-XX:MaxTenuringThreshold=
15
|
jinfo还可以在运行时修改参数值:
1
2
3
4
5
|
> jinfo -flag PrintGCDetails
31518
-XX:-PrintGCDetails
> jinfo -flag +PrintGCDetails
31518
> jinfo -flag PrintGCDetails
31518
-XX:+PrintGCDetails
|
1.4、jmap命令
jmap命令主要用于生成堆快照文件,它的使用方法如下:
1
2
3
|
> jmap -dump:format=b,file=heap.hprof
31531
Dumping heap to /Users/caojie/heap.hprof ...
Heap dump file created
|
获得堆快照文件之后,我们可以使用多种工具对文件进行分析,例如jhat,visual vm等。
1.5、jhat命令
使用jhat工具可以分析Java应用程序的堆快照文件,使用命令如下:
1
2
3
4
5
6
7
8
9
10
|
> jhat heap.hprof
Reading from heap.hprof...
Dump file created Tue Nov
11
06
:
02
:
05
CST
2014
Snapshot read, resolving...
Resolving
8781
objects...
Chasing references, expect
1
dots.
Eliminating duplicate references.
Snapshot resolved.
Started HTTP server on port
7000
Server is ready.
|
jhat在分析完成之后,使用HTTP服务器展示其分析结果,在浏览器中访问http://127.0.0.1:7000/即可得到分析结果。
1.6、jstack命令
jstack可用于导出Java应用程序的线程堆栈信息,语法为:
1
|
jstack -l <pid>
|
jstack可以检测死锁,下例通过一个简单例子演示jstack检测死锁的功能。java代码如下:
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
|
public
class
DeadLock
extends
Thread {
protected
Object myDirect;
static
ReentrantLock south =
new
ReentrantLock();
static
ReentrantLock north =
new
ReentrantLock();
public
DeadLock(Object obj) {
this
.myDirect = obj;
if
(myDirect == south) {
this
.setName(
"south"
);
}
if
(myDirect == north) {
this
.setName(
"north"
);
}
}
@Override
public
void
run() {
if
(myDirect == south) {
try
{
north.lockInterruptibly();
try
{
Thread.sleep(
500
);
}
catch
(Exception e) {
e.printStackTrace();
}
south.lockInterruptibly();
System.out.println(
"car to south has passed"
);
}
catch
(InterruptedException e1) {
System.out.println(
"car to south is killed"
);
}
finally
{
if
(north.isHeldByCurrentThread())
north.unlock();
if
(south.isHeldByCurrentThread())
south.unlock();
}
}
if
(myDirect == north) {
try
{
south.lockInterruptibly();
try
{
Thread.sleep(
500
);
}
catch
(Exception e) {
e.printStackTrace();
}
north.lockInterruptibly();
System.out.println(
"car to north has passed"
);
}
catch
(InterruptedException e1) {
System.out.println(
"car to north is killed"
);
}
finally
{
if
(north.isHeldByCurrentThread())
north.unlock();
if
(south.isHeldByCurrentThread())
south.unlock();
}
}
}
public
static
void
main(String[] args)
throws
InterruptedException {
DeadLock car2south =
new
DeadLock(south);
DeadLock car2north =
new
DeadLock(north);
car2south.start();
car2north.start();
Thread.sleep(
1000
);
}
}
|
使用jps命令查看进程号为32627,然后使用jstack -l 32637 > a.txt命令把堆栈信息打印到文件中,该文件内容如下:
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
2014
-
11
-
11
21
:
33
:
12
Full thread dump Java HotSpot(TM)
64
-Bit Server VM (
24.55
-b03 mixed mode):
"Attach Listener"
daemon prio=
5
tid=
0x00007f8d0c803000
nid=
0x3307
waiting on condition [
0x0000000000000000
]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"DestroyJavaVM"
prio=
5
tid=
0x00007f8d0b80b000
nid=
0x1903
waiting on condition [
0x0000000000000000
]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"north"
prio=
5
tid=
0x00007f8d0c075000
nid=
0x5103
waiting on condition [
0x0000000115b06000
]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait
for
<
0x00000007d55ab600
> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:
186
)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:
834
)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:
894
)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:
1221
)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:
340
)
at DeadLock.run(DeadLock.java:
48
)
Locked ownable synchronizers:
- <
0x00000007d55ab5d0
> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
"south"
prio=
5
tid=
0x00007f8d0c074800
nid=
0x4f03
waiting on condition [
0x0000000115a03000
]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait
for
<
0x00000007d55ab5d0
> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:
186
)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:
834
)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:
894
)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:
1221
)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:
340
)
at DeadLock.run(DeadLock.java:
28
)
Locked ownable synchronizers:
- <
0x00000007d55ab600
> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
"Service Thread"
daemon prio=
5
tid=
0x00007f8d0c025800
nid=
0x4b03
runnable [
0x0000000000000000
]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C2 CompilerThread1"
daemon prio=
5
tid=
0x00007f8d0c025000
nid=
0x4903
waiting on condition [
0x0000000000000000
]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C2 CompilerThread0"
daemon prio=
5
tid=
0x00007f8d0d01b000
nid=
0x4703
waiting on condition [
0x0000000000000000
]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"Signal Dispatcher"
daemon prio=
5
tid=
0x00007f8d0c022000
nid=
0x4503
runnable [
0x0000000000000000
]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"Finalizer"
daemon prio=
5
tid=
0x00007f8d0d004000
nid=
0x3103
in Object.wait() [
0x000000011526a000
]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <
0x00000007d5505568
> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:
135
)
- locked <
0x00000007d5505568
> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:
151
)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:
189
)
Locked ownable synchronizers:
- None
"Reference Handler"
daemon prio=
5
tid=
0x00007f8d0d001800
nid=
0x2f03
in Object.wait() [
0x0000000115167000
]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <
0x00000007d55050f0
> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:
503
)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:
133
)
- locked <
0x00000007d55050f0
> (a java.lang.ref.Reference$Lock)
Locked ownable synchronizers:
- None
"VM Thread"
prio=
5
tid=
0x00007f8d0b83b000
nid=
0x2d03
runnable
"GC task thread#0 (ParallelGC)"
prio=
5
tid=
0x00007f8d0b818000
nid=
0x2503
runnable
"GC task thread#1 (ParallelGC)"
prio=
5
tid=
0x00007f8d0b819000
nid=
0x2703
runnable
"GC task thread#2 (ParallelGC)"
prio=
5
tid=
0x00007f8d0d000000
nid=
0x2903
runnable
"GC task thread#3 (ParallelGC)"
prio=
5
tid=
0x00007f8d0d001000
nid=
0x2b03
runnable
"VM Periodic Task Thread"
prio=
5
tid=
0x00007f8d0c02e800
nid=
0x4d03
waiting on condition
JNI global references:
109
Found one Java-level deadlock:
=============================
"north"
:
waiting
for
ownable synchronizer
0x00000007d55ab600
, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by
"south"
"south"
:
waiting
for
ownable synchronizer
0x00000007d55ab5d0
, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by
"north"
Java stack information
for
the threads listed above:
===================================================
"north"
:
at sun.misc.Unsafe.park(Native Method)
- parking to wait
for
<
0x00000007d55ab600
> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:
186
)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:
834
)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:
894
)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:
1221
)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:
340
)
at DeadLock.run(DeadLock.java:
48
)
"south"
:
at sun.misc.Unsafe.park(Native Method)
- parking to wait
for
<
0x00000007d55ab5d0
> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:
186
)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:
834
)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:
894
)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:
1221
)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:
340
)
at DeadLock.run(DeadLock.java:
28
)
Found
1
deadlock.
|
从这个输出可以知道:
1、在输出的最后一段,有明确的"Found one Java-level deadlock"输出,所以通过jstack命令我们可以检测死锁;
2、输出中包含了所有线程,除了我们的north,sorth线程外,还有"Attach Listener", "C2 CompilerThread0", "C2 CompilerThread1"等等;
3、每个线程下面都会输出当前状态,以及这个线程当前持有锁以及等待锁,当持有与等待造成循环等待时,将导致死锁。
1.7、jstatd命令
jstatd命令是一个RMI服务器程序,它的作用相当于代理服务器,建立本地计算机与远程监控工具的通信,jstatd服务器能够将本机的Java应用程序信息传递到远程计算机,由于需要多台计算机做演示,此处略。
1.8、hprof工具
hprof工具可以用于监控Java应用程序在运行时的CPU信息和堆信息,关于hprof的官方文档如下:https://docs.oracle.com/javase/7/docs/technotes/samples/hprof.html
2、Visual VM工具
Visual VM是一个功能强大的多合一故障诊断和性能监控的可视化工具,它集成了多种性能统计工具的功能,使用Visual VM可以替代jstat、jmap、jhat、jstack等工具。在命令行输入jvisualvm即可启动visualvm。
打开Visual VM之后,左边导航栏会显示出当前机器所有Java进程:
点击你想监控的程序即可对该程序进行监控,Visual VM的性能监控页一共有以下几个tab页:
概述页会显示程序的基本使用情况,比如,进程ID,系统属性,启动参数等。
通过监视页面,可以监视应用程序的CPU、堆、永久区、类加载器和线程数的整体情况,通过页面上的Perform GC和Heap Dump按钮还可以手动执行Full GC和生成堆快照。
线程页面会提供详细的线程信息,单击Thread Dump按钮可以导出当前所有线程的堆栈信息,如果Visual VM在当前线程中找到死锁,则会以十分显眼的方式在Threads页面给予提示。
抽样器可以对CPU和内存两个性能进行抽样,用于实时地监控程序。CPU采样器可以将CPU占用时间定位到方法,内存采样器可以查看当前程序的堆信息。下面是一个频繁调用的Java程序,我们会对改程序进行采样:
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
77
78
|
public
class
MethodTime {
static
java.util.Random r=
new
java.util.Random();
static
Map<String,String> map=
null
;
static
{
map=
new
HashMap<String,String>();
map.put(
"1"
,
"Java"
);
map.put(
"2"
,
"C++"
);
map.put(
"3"
,
"Delphi"
);
map.put(
"4"
,
"C"
);
map.put(
"5"
,
"Phython"
);
}
public
String getNameById(String id){
try
{
Thread.sleep(
1
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
return
map.get(id);
}
public
List<String> getNamesByIds(String ids){
List<String> re=
new
ArrayList<String>();
String[] strs=ids.split(
","
);
for
(String id:strs){
re.add(getNameById(id));
}
return
re;
}
public
List<String> getNamesByIdsBad(String ids){
List<String> re=
new
ArrayList<String>();
String[] strs=ids.split(
","
);
for
(String id:strs){
//A bad code
getNameById(id);
re.add(getNameById(id));
}
return
re;
}
public
class
NamesByIdsThread
implements
Runnable{
@Override
public
void
run() {
try
{
while
(
true
){
int
c=r.nextInt(
4
);
String ids=
""
;
for
(
int
i=
0
;i<c;i++)
ids=Integer.toString((r.nextInt(
4
)+
1
))+
","
;
getNamesByIds(ids);
}
}
catch
(Exception e){
}
}
}
public
class
NamesByIdsBadThread
implements
Runnable{
@Override
public
void
run() {
try
{
while
(
true
){
int
c=r.nextInt(
4
);
String ids=
""
;
for
(
int
i=
0
;i<c;i++)
ids=Integer.toString((r.nextInt(
4
)+
1
))+
","
;
getNamesByIdsBad(ids);
}
}
catch
(Exception e){
}
}
}
public
static
void
main(String args[]){
MethodTime instance=
new
MethodTime();
new
Thread(instance.
new
NamesByIdsThread()).start();
new
Thread(instance.
new
NamesByIdsBadThread()).start();
}
}
|
通过Visual VM的采样功能,可以找到改程序中占用CPU时间最长的方法:
默认Visual VM不统计内置对象的函数调用,比如java.*包中的类,如果要统计这些内置对象,单机右上角的设置进行调配。Visual VM虽然可以统计方法的调用时间,但是无法给出方法调用堆栈,Jprofile不仅可以给出方法调用时间,还可以给出方法调用堆栈,较Visual VM更强大。
右击左导航的应用程序,会出现以下菜单:
单机应用程序快照,可以分析当前应用程序的快照,单击堆Dump能够对当前的堆信息进行分析。Visual VM的更多使用方法,可以查看Oracle的官方文档https://docs.oracle.com/javase/7/docs/technotes/guides/visualvm/index.html
BTrace插件
BTrace是一款功能强大的性能检测工具,它可以在不停机的情况下,通过字节码注入,动态监控系统的运行情况,它可以跟踪指定的方法调用、构造函数调用和系统内存等信息,本部分打算举一个例子,讲解一下BTrace的使用。要在Visual VM中使用Btrace,首先需要安装Btrace插件,点击工具->插件即可在线安装,安装后右键应用程序,就会出现如下选项:
点击Trace application,即可进入BTrace插件界面。使用BTrace可以监控指定函数的耗时,以下脚本通过正则表达式,监控所有类的getNameById方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import
com.sun.btrace.annotations.*;
import
static
com.sun.btrace.BTraceUtils.*;
@BTrace
public
class
TracingScript {
@TLS
private
static
long
startTime =
0
;
@OnMethod
(clazz=
"/.+/"
, method=
"/getNameById/"
)
//监控任意类的getNameById方法
public
static
void
startMethod() {
startTime=timeMillis();
}
@OnMethod
(clazz=
"/.+/"
, method=
"/getNameById/"
,
location=
@Location
(Kind.RETURN))
//方法返回时触发
public
static
void
endMethod() {
print(strcat(strcat(name(probeClass()),
"."
), probeMethod()));
print(
" ["
);
print(strcat(
"Time taken : "
, str(timeMillis() - startTime)));
println(
"]"
);
}
}
|
点击运行,部分输出如下:
1
2
3
4
|
MethodTime.getNameById [Time taken :
5
]
MethodTime.getNameById [Time taken :
4
]
MethodTime.getNameById [Time taken :
7
]
MethodTime.getNameById [Time taken :
7
]
|
BTrace除了可以监控函数耗时外,还可以指定程序运行到某一行代码触发某一行为,定时触发行为,监控函数参数等等。
3、MAT内存分析工具
MAT是一款功能强大的Java堆内存分析器,可以用于查找内存泄露以及查看内存消耗情况,MAT的官方文档如下:http://help.eclipse.org/luna/index.jsp?topic=/org.eclipse.mat.ui.help/welcome.html。
在MAT中有浅堆和深堆的概念,浅堆是指一个对象结构所占用的内存大小,深堆是指一个对象被GC回收后可以真正释放的内存大小。
通过MAT,可以列出所有垃圾回收的根对象,Java系统的根对象可能是以下类:系统类,线程,Java局部变量,本地栈等等。在MAT中还可以很清楚的看到根对象到当前对象的引用关系链。
MAT还可以自动检测内存泄露,单击菜单上的Leak Suspects命令,MAT会自动生成一份报告,这份报告罗列了系统内可能存在内存泄露的问题点。
在MAT中,还可以自动查找并显示消耗内存最多的几个对象,这些消耗大量内存的大对象往往是解决系统性能问题的关键所在。
具体例子,略,网速太慢,至今还未下好。。