虚拟机性能监控与故障处理工具
文章目录
Java与C++之间有一堵内存动态分配和垃圾收集技术所围成的“高墙”,墙外面的人想进去,墙里面的人想出来。
本文内容基本来自于《深入理解Java虚拟机-JVM高级特性与最佳实践》第4章,是对这本书学习的笔记
概述
理论总是作为指导实践的工具,能把这些知识应用到实际工作中才是我们的最终目的。给系统定位问题的时候,知识、经验是关键基础,数据是依据,工具是运用知识处理数据的手段。
JDK的命令行工具
1 jps:虚拟机进程状况工具
JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。(从名字和功能都与UNIX的ps类似)
2 jstat:虚拟机统计信息监视工具
JVM Statistics Monitoring Tool,用于收集HotSpot虚拟机进程。
3 jinfo:Java配置信息工具
Configuration Info for Java,显示虚拟机配置信息。
4 jmap:Java内存映像工具
Memory Map for Java,生成虚拟机的内存转储快照(heapdump文件)。
5 jhat:虚拟机堆转储快照分析工具
JVM Heap Dump Browser,用于分析heapdump文件,它会建立一个HTTP/HTML服务器,让用户可以在浏览器上查看分析结果。
6 jstack:Java堆栈跟踪工具
Stack Trace for Java,显示虚拟机的线程快照。
7 HSDIS:JIT生成代码反汇编
当我们分析程序的执行语义问题(虚拟机作了什么)时,在字节码层面上分析完全可行,但在分析程序的执行行为问题(虚拟机时怎样做的、性能如何)时,由于JIT编译器的存在,在字节马层面上分析就没有什么意义了,需要通过其他方式解决。
HSDIS是一个Sun官方推荐的HotSpot虚拟机JIT编译代码的反汇编插件,它包含在HotSpot虚拟机的源码之中,但没有提供编译后的程序。《深入理解Java虚拟机》(第二版,2013.6)这本书说可以在下面网站中下载到单独的源码:
http://kenai.com/projects/base-hsdis
但现在这网站似乎已经被关闭了。可以自己找类似下面的资料试一下:
https://blog.csdn.net/VimGuy/article/details/81879210
HSDIS的作用是让Hotpot的-XX:+PrintAssembly指令调用它来把动态生成的本地代码还原为汇编代码输出,同时还生成了大量非常有价值的注释,这样我们就可以通过输出的代码来分析问题。
JDK的可视化工具
JConsole:Java监视与管理控制台
1 启动JConsole
打开$JAVA_HOME/bin/jconsole即可。
概述页签显示整个虚拟机主要运行数据的概览,其中包括“堆内存使用情况”、“线程”、“类”、“CPU使用情况”4中信息的曲线图。
2 内存监控
相当于可视化的jstat命令,用于监视收集器管理的虚拟机内存(Java堆和永久代)的变化趋势。
下面是示例代码和监视效果,可以从中体会关于内存的基础知识:
import java.util.ArrayList;
import java.util.List;
public class MemoryListenTest {
static class OOMObject{
public byte[] placeHolder = new byte[64 * 1024];
}
public static void fillHeap(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();
}
/**
* VM args:-Xms100m -Xmx100m -XX:+UseSerialGC
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
System.out.println("start");
Thread.sleep(10 * 1000);
fillHeap(1000);
System.gc();
System.out.println("end");
Thread.sleep(100 * 1000);
}
}
3 线程监控
"线程”页签相当于可视化的jstack命令,遇到线程停顿时可以使用这个页签进行监控分析。线程长时间停顿的主要原因有:等待外部资源(数据库连接、网络资源、设备资源)、死循环、锁等待(活锁和死锁)。
示例代码如下:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class ThreadListenTest {
/**
* 线程死循环演示
*/
public static void createBusyThread() {
new Thread(() -> {
while (true) {
;
}
}, "testBusyThread").start();
}
/**
* 线程锁等待演示
*/
public static void createLockThread(final Object lock) {
new Thread(() -> {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "testLockThread").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);
}
}
可以在Jconsole中查看到main、testBusyThread、testLockThread线程的状态。
死锁示例如下:
public class DeadLockTreadTest {
/**
* 线程死锁等待演示
*/
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-15与Thread12形成死锁:
VisualVM:多合一故障处理工具
VisualVM(All-inOne Java Troubleshooting Tool)时到目前(2013.6)为止随JDK发布的功能最强大的运行监视和故障处理程序,官方在软件说明中写上了“All-in-One”的描述字样,预示着它除了运行监视、故障处理外,还提供了很多其他方面的功能,如性能分析(Profiling)。
打开${JAVA_HOME}/bin/jvisualvm即可使用这一工具。
详见:https://visualvm.github.io/gettingstarted.html?Java_VisualVM