虚拟机性能监控可视化工具


前言

JDK除了提供大量的命令行之外,还提供两个强大的可视化工具Jconsole(JDK1.5提出来的)和VisualVM(JDK1.6提出来的,并且已经从JDK分离出来成为可以独立发展的开源项目)

1、jconsole

1、启动jconsole,启动之前我已经启动一个SpringBoot的web项目了

1
healerjean$ jconsole

WX20180410-172624@2x

WX20180410-172942@2x

2、内存监控

内存页,相当于可视化的jstat(jvm虚拟机统计信息监控),用于监控收集器管理的虚拟机内存(java堆和永久代)的变化趋势

2.1、代码测试

设置JVM参数

1
-Xms100m -Xmx100m -XX:+UseSerialGC
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
package com.hlj.jvm.JvmBin.JvmJconsole;

/**
* @Description JVM Jsoncole内存页 测试
* @Author HealerJean
* @Date 2018/4/10 下午5:35.
*/

import java.util.ArrayList;
import java.util.List;

class JvmJconsole {

/**
* 内存占位符对象,一个OOMObject大约占64K
*/
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();
}

public static void main(String[] args) throws Exception {
fillHeap(1000);
}

}


2.2、点击Jconsole查看内存 Eden

WX20180410-182108@2x

1、同JVM参数判断下老年代有多大内存
答:
-Xms100m -Xmx100m 可以知道Java堆区最大为100M 而上面的Eden Space最大为27328KB,所有新生代所占有的内存应该为 (27328/8 * 10 ) 堆区减去它就是老年代的内存大小了

2、执行完Systam.gc()之后老年代的柱状图依然显示峰值状态,代码需要如何调整才能让System.gc()回收掉填充到堆中的对象 Tenured Gen(老年代-养老区)看下图 需要注意的是非堆不是栈哦

答:因为在执行System.gc()之后 ,方法fillHeap并没有退出,因此 list对象在System.gc()执行时仍然处于作用域之内,将System.gc() 放到fillHeap 方法外就可以会受到全部内存

WX20180410-183110@2x

3、线程监控

这里的线程监控相当于是 jstack标签,遇到线程停顿时可以使用这个页签进行监控分析,签名讲解jstack命令的时候提到过线程长时停顿的主要原因有:等待外部资源(数据库连接,网络资源,设备资源、死循环,锁等待(活锁和死锁))

1、代码测试

这里开启两个线程,一个是,死循环while循环等待,一个是wait等待释放资源

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
package com.hlj.jvm.JvmBin.JvmJconsole;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
* @Description 线程死循环和wait演示
* @Author HealerJean
* @Date 2018/4/10 下午6:52.
*/

public class JConsoleThreadWhileTest {
/**
* 线程一直跑while
*/
public static void createBusyThread() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// 状态: RUNNABLE 会在空循环上用尽全部执行时间直到线程切换,这种等待会消耗较多的CPU资源
while (true) {

}
}
}, "testBusyThread");
thread.start();
}

/**
* 锁等待测试
*
* @param lock
*/
public static void createLockThread(final Object lock) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
try {
// 线程等待着lock对象的notify或notifyAll方法的出现,线程这时间处于waiting状态,在被唤醒前不会被分配执行时间。
// 处理活锁状态,只要lock对象的notify或notifyAll方法出现,这个线程便能激活断续执行,
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "testLockThread");
thread.start();
}


public static void main(String[] args) throws IOException, InterruptedException {
// 控制台输入
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
br.readLine();
createBusyThread();// 线程一直跑while
br.readLine();
Object obj = new Object();
createLockThread(obj);// 锁等待测试
Thread.sleep(10000000);
}
}

1、运行main函数,观察到堆栈追踪 到readBytes等待控制台输入


WX20180410-190800@2x

2、控制台输入 你好,出现了哪个循环等待的线程线程(会在空循环上用尽全部执行时间直到线程切换,这种等待会消耗较多的cpu资源),状态为RUNNALBE,在程序的低22行等待,22行为

1
while (true) {

![WX20180410-191056@2x](https://raw.githubusercontent.com/HealerJean/HealerJean.github.io/master/blogImages/WX20180410-191056@2x.png)

3、再观察线程main,堆栈追踪到了59行,第二个等待控制台书输入

1
br.readLine();

WX20180410-191247@2x

4、控制台输入hello,观察到又开启了另外一个线程testLockThread,发现他的状态为warning(警告状态。在被唤醒钱不会分配执行时间) 栈堆追踪到了43行

1
2
lock.wait();


WX20180410-191425@2x

3.2、死锁代码样例

下面的代码我只做了200个线程,目的是为了提高发送死锁的可能性,有时候,其实几个就够了的,但是概率不大,为了模拟,所以200个基本上肯定会发送死锁的,不信你就看呗

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
package com.hlj.jvm.JvmBin.JvmJconsole;

/**
* @Description 线程监控测试,死锁测试
* @Author HealerJean
* @Date 2018/4/10 下午7:20.
*/
public class JConsoleThreadTest {

static class synRun implements Runnable {
int a;
int b;

public synRun(int a, int b) {
this.a = a;
this.b = b;
}

@Override
public void run() {
// Integer.valueOf(a) 会换存-128~127的数字,实际就返回了2和3两个对象
synchronized (Integer.valueOf(a)) {
// 假如在两个synchronized之间发生了线程切换,那就会出现线程a等待线程b的(Integer.valueOf(b))对象,
// 而线程b又等待线程a的(Integer.valueOf(a))的对象,结果都跑不下去了,线程卡住,都等不了对方释放锁了
synchronized (Integer.valueOf(b)) {
System.out.println(a + " + " + b + "=" + (a + b));
}
}
}
}

public static void main(String[] args) throws InterruptedException {
Thread.sleep(30000);// 30秒,有空余时间来启动,jconsole,并链接这个java进程
System.out.println("start------");
// 200个线程
for (int i = 0; i < 100; i++) {
new Thread(new synRun(2, 3)).start();
new Thread(new synRun(3, 2)).start();
}
System.out.println("end------");
Thread.sleep(10000000);// 一直停顿,方便查看数据

}

}

1、打开Jconsole 会发现出现了很多线程

WX20180410-192818@2x

2、点击检查死锁,就会出现发生死锁的的线程了

WX20180410-192859@2x

点击 201 会发现状态是block 堆栈追踪到了25行,如下代码,它的拥有者为Thread-5,同理观察其他的两个死锁线程,这样就相互卡主不存在相互释放的希望了

synchronized (Integer.valueOf(b)) {

WX20180410-193001@2x

WX20180410-193129@2x

WX20180410-193155@2x

2、VisualVM 多合一故障处理工具

2.1、软件安装与插件安装

根据自己版本安装-下载地址

1、安装好之后打开,安装插件,不然像个秃子一样,

WX20180410-194514@2x

2、如果发现不能下载那就是下载插件与jdk版本的问题,打开下面的链接,根据jdk版本选择合适的插件主url下载,我的是jdk1.8.0

https://visualvm.github.io/pluginscenters.html
WX20180411-101625@2x

3、复制下面这个url,放到插件设置中去,然后选择可以安装的即可

WX20180411-101810@2x

WX20180411-101957@2x

2.2、VisualVM

概述,监视,线程,MBeans的功能和Jconsole的功能差不多,主要是这里添加了很多插件,着重介绍下插件吧

2.2.1、生成,浏览器堆存储快照

2.2.2、Btrace(动态日志追踪) 回家再widows上操作吧,哎

它的作用是在不停止目标程序运行的前提下,通过hotspot虚拟机的Hotswap技术动态加入原来并不存在的调试代码,这项工具在实际的生产中程序操作意义很大:

出现由来:
经常遇到程序出了问题,在排查的时候,关于错误的一些必要信息,比如方法参数,返回值等在开发的时候,并没有打印到日志之中,以至于不得不停掉服务,通过调试增量来加入日志代码来解决问题。但是::::

有了它在应用程序


Author: jony
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source jony !
 Previous
JVM参数 JVM参数
前言Java堆大小为20M 不可扩展(Xms表示初始Java堆大小 Xmx为Java堆最大 这里设置相等,就表明不可以扩展) Xmn 表示分给新生代 (下面表示分给新生到10M,那么剩余的就分配给了老年代) 12345-verbos
2018-04-11
Next 
虚拟机性能监控与故障处理工具 虚拟机性能监控与故障处理工具
前言这个章节相当关键,因为前面4篇文章主要介绍了关于虚拟机内存分配和回收技术各方面的内容工具是运用知识处理数据的手段 JDK的命令行工具对于一般的程序员来说,其实我们知道的有两个命令工具java.exe 和javac.exe,但是其他的
2018-04-10
  TOC