JVM 工具
1. 基础故障处理工具
1.1 jps(虚拟机进程状况工具)
jps
的功能与UNIX
中的ps
命令类似。
可以列出正在运行的虚拟机进程,并显式虚拟机指向主类(main
函数所在的类)名称以及这些进程在本地虚拟机唯一ID
。
命令格式
jps [options] [hostid]
jps
还可以通过RMI
协议查询开启了RMI
服务的远程虚拟机进程状态,参数hostid
为RMI
注册表中注册的主机名。
选项 | 作用 |
---|---|
-q | 只输出java 在本地虚拟机的唯一id |
-m | 输出虚拟机进程启动时传递给主类main() 函数的参数 |
-l | 输出主类的全名,如果进程执行的是Jar 包,输入Jar 路径 |
-v | 输出虚拟机进程启动时JVM 参数 |
示例
1.2 Jstat(虚拟机统计信息监视工具)
Jstat
用于监视虚拟机各种运行状态信息。可以显式虚拟机进程中的类加载、内存、垃圾回收,即时编译等运行数据。
命令格式
jstat [ option vmid [ interval [ s | ms ] [ count ]]]
如果是本地虚拟机进程,VMID
则为jps
查出的进程对应的id
。
如果是远程虚拟机进程,那么VMID
的格式应是:[protocol:] [//] lvmid [@hostname [:port]/servername]
interval
和 count
代表查询间隔和次数,如果省略,则说明只查询一次。
option
代表用户查询虚拟机的信息,主要分为类装载、垃圾收集、运行期编译状况。
选项 | 作用 |
---|---|
-class | 监视类装载、装载数量、总空间及类装载耗费时间 |
-gc | 监视Java堆状况,包括Eden区,两个sruvivor区、老年代、永久代等容量、医用空间、GC时间合计 |
-gccapacity | 监视内容与-gc基本相同,但输出主要关注Java堆各个取余使用的最大、最小空间 |
-gcutil | 监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比 |
-gccause | -gcutil功能一样,但是会额外输出导致上一次GC产生的原因 |
-gcnew | 监视新生代GC状况 |
-gcnewcapacity | 监视内存与-gcnew基本相同,输出主要关注使用到的最大、最小空间 |
-gcold | 监视老生代GC状况 |
-gcoldcapacity | 监视内容与-gcold基本相同,输出主要关注使用到的最大、最小空间 |
-gcpermcapacity | 输出永久代使用的最大、最小空间 |
-comiler | 输出JIT编译器编译过的方法、耗时等信息 |
-printcompilation | 输入已经被JIT编译的方法 |
示例
E
:新生代Eden
区,使用了8.01%
空间
S0、S1
:表示两个Survivor区
O
:老年代
M
:元数据区使用比例
CCS
:压缩使用比例
YGC
:年轻代垃圾回收次数
YGCT
:年轻代垃圾回收消耗时间
FGC
:老年代垃圾回收次数
FGCT
:老年代垃圾回收消耗时间
GCT
:垃圾回收消耗总时间
1.3 jinfo(Java 配置信息工具)
jinfo
的作用是实时查看和调整虚拟机各项参数。
命令格式
jinfo [option] pid
选项 | 作用 |
---|---|
no option | 输出全部的参数和系统属性 |
-flag name | 输出对应名称的参数 |
-flag [+|-] name | 开启或者关闭对应名称的参数 |
-flags | 输出全部的参数 |
-sysprops | 输出系统属性 |
示例
1.4 jmap(Java 内存映像工具)
jmap
最主要的作用是获取对转储快照,除此之外,还可以查询finalize
执行队列、Java
堆和方法区的详细信息,如空间使用率,当前用到是哪种收集器。
命令格式
jmap [option] vmid
选项 | 作用 |
---|---|
-dump | 生成 Java 堆转储快照。格式为 -dump:[live,]format=b,file=<filename> ,其中 live 子参数说明是否只dump 出存活的对象 |
-finalizerinfo | 显示在F-Queue 队列等待Finalizer 线程执行finalize 方法的对象 |
-heap | 显示Java堆详细信息 |
histo[:live] | 显示堆中对象的统计信息 |
F | 当-dump没有响应时,使用-dump或者-histo参数,在这个模式下,live子参数无效. |
示例
1.5 Jhat(虚拟机堆转储快照分析工具)
jhat
命令与jmp
搭配使用,来分析jmap
生成的堆转储快照。
1.6 jstack(Java 堆栈跟踪工具)
jstack
用于生成虚拟机当前时刻的线程快照,生成线程快照的目的通常是定位线程长时间停顿的原因,如线程间死锁、死循环、请求外部资源等导致的长时间挂起等,都是导致线程长时间停顿的常见原因。
通过jstack
可以获知没有响应的线程在后台做些什么事情,或者等待着什么资源。
命令格式
jstack [option] vmid
选项 | 说明 |
---|---|
-F | 当正常输出的请求不被响应时,强制输出线程堆栈 |
-l | 除堆栈外,显示关于所的附加信息 |
-m | 如果调用到本地方法的话,可以显示 C/C++ 的堆栈 |
示例
2. 可视化故障处理工具
2.1 JConsole(Java 监控与管理控制台)
JConsole
是一个堆Java
进程进行监视、管理的可视化工具。
2.1.1 启动 JConsole
2.1.2 内存监控
"内存"页签的作用相当于可视化的jstat
命令,用于监视被虚拟机管理的虚拟内存的变化趋势。
运行如下代码,同时设置虚拟机参数为:-Xms100m -Xmx100m -XX:+UseSerialGC
import java.util.ArrayList;
import java.util.List;
/**
* @author wangzhao
* @date 2020/7/3 22:27
*/
public class MemoryAnalysis {
static class OOMObject{
public byte[] placeholder = new byte[64 * 1024];
}
public static void fileHeap(int num) throws InterruptedException {
List<OOMObject> list = new ArrayList<OOMObject>();
for (int i = 0; i < num; i++){
// 稍作演示,令监视曲线的变化更加明显
Thread.sleep(50);
list.add(new OOMObject());
}
System.gc();
}
public static void main(String[] args) throws InterruptedException {
fileHeap(1000);
}
}
2.1.3 线程监控
"线程"页签的作用相当于可视化的jstack
命令,遇到线程停顿的时候可以使用这个页签的功能进行分析。
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.BufferedReader;/**
* @author wangzhao
* @date 2020/7/3 22:35
*/
public class ThreadAnalysis {
// 线程死循环演示
public static void createBusyThread(){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true)
;
}
}, "testBusyThread");
thread.start();
}
// 线程锁等待演示
public static void createLockThread(final Object lock){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "testLockThread");
thread.start();
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
br.readLine();
createBusyThread();
br.readLine();
Object obj = new Object();
createLockThread(obj);
}
}
main
线程中,堆栈追踪显式BufferedReader
的readBytes()
正在等待System.in
的键盘输出,这时候线程为Runnable
状态,Runnable
状态的线程仍会被分配运行时间。但readBytes()
检查到流没有更新就立刻归还执行令牌给操作系统,这种等待只消耗很小的处理器资源。
testBusyThread
线程一直在执行空循环,可以看到其停留在16
行,16
的代码为while (true)
的循环体。这时候线程为Runnable
状态,而且没有归还线程执行令牌的动作,所以会在空循环耗尽操作系统分配给它的执行时间,直到线程切换为止,这种等待会消耗大量的处理器资源。
testLockThread
线程在等待lock
对象的notify()
或notifyAll()
方法的出现,线程这时候处于WAITING
状态,在重新唤醒前不会被分配执行时间。
死锁示例:
/**
* @author wangzhao
* @date 2020/7/3 22:49
*/
public class DeadLockAnalysis {
static class SynAddRunnable implements Runnable{
int a, b;
public SynAddRunnable(int a, int b){
this.a = a;
this.b = b;
}
@Override
public void run() {
synchronized (Integer.valueOf(a)) {
synchronized (Integer.valueOf(b)){
System.out.println(a + b);
}
}
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++){
new Thread(new SynAddRunnable(1, 2)).start();
new Thread(new SynAddRunnable(2, 1)).start();
}
}
}
由于Thread-56
和Thread-65
分别在等待对方释放锁,从而形成死锁。
2.2 VisualVM(多合一故障处理工具)
除了运行监视、故障处理外,还可以进行性能分析、生成、浏览对转储快照、BTrace动态日志跟踪等。
由于需要自行去下载,故这里不再介绍。