Jconsole: JAVA 监视和管理控制台简介

时间:2022-01-23 17:00:43

###Jconsole: JAVA 监视和管理控制台简介
JDK中除了提供大量的命令行之外,还提供两个功能强大的可视化工具:JConsole和VisualVM。 之前对java的调试一直停留在 右键->debug as ,实在惭愧,今天看了下,索性当个读书笔记记录,果然我JAVA大法好,hhh......

启动JConsole

通过D:\Program Files\Java\jdk1.7.0_71\bin找到jconsole.exe启动,这个位置取决于本机安装的java路径。类似jps命令,显示本机运行的所有虚拟机进程。可支持本地进程和远程进程。

Jconsole: JAVA 监视和管理控制台简介

双击选择一个进程,看到如下:

Jconsole: JAVA 监视和管理控制台简介

“概述”显示整个虚拟机主要运行数据的概览,其中包括“堆内存使用情况”、“线程”、“类”、“CPU使用情况”4中信息的曲线图,这些曲线图是后面“内存”、“线程”、“类”页签的信息汇总。

内存监控

相当于可视化的jstat命令,用于监视受收集器管理的虚拟机内存(JAVA堆和永久代)的变化趋势。

Jconsole: JAVA 监视和管理控制台简介

线程监控

相当于可视化jstack命令,遇到线程停顿时可是用这个页签进行监控分析。而jstack命令中 线程长时间停顿的主要原因有:

等待外部资源(数据库连接,网络资源,设备资源等)

死循环

锁等待(活锁和死锁)

通过例子进行说明:

代码如下:

package com.xjtu.imiss.chapter4;

import java.io.BufferedReader;
import java.io.InputStreamReader; public class MonitoringTest { /**
* 线程死循环演示
*/
public static void createBusyThread() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true);// 第15行
}
}, "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 Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
br.readLine();
createBusyThread();
br.readLine();
Object obj = new Object();
createLockThread(obj);
}
}

程序运行后,首先在“线程”页签选择main线程,如下所示:

Jconsole: JAVA 监视和管理控制台简介

堆栈追踪显示BufferedReader在readBytes方法中等待System.in的键盘输入,这时线程为Runnable状态,Runnable状态的线程会被分配运行时间,但readBytes方法检查到流没有更新时候就会归还执行令牌,这种等待只消耗很小的CPU资源。

下来看testBusyThread线程,一直执行空循环,从状态中看到代码停留在while(true)这一行,此时线程为Runnable装啊提,而且没有归还线程令牌的动作会在空循环用尽全部执行时间,直到线程切换,这种等待比较消耗CPU资源。

Jconsole: JAVA 监视和管理控制台简介

下来是testLockThread线程等到这lock对象的notify 或者notifyAll方法的出现,此时线程处于waiting状态,在被唤醒之前不会被分配执行时间。

Jconsole: JAVA 监视和管理控制台简介

testLockThread处于正常的活锁等待,只要lock对象的notify()或者notifyAll()方法被调用,这个线程就能激活并继续执行。下面演示一个无法再被激活的死锁等待。

package com.xjtu.imiss.chapter4;

public class MonitoringTest {
/**
* 线程死锁等待演示
*/
static class SynAddRunalbe implements Runnable {
int a, b; public SynAddRunalbe(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 SynAddRunalbe(1, 2)).start();
new Thread(new SynAddRunalbe(2, 1)).start();
}
} }

这段代码开了200个线程分别计算1+2以及2+1的值,其实for循环可省略,因为两个线程也能导致死锁,不过那样概率很小,需要尝试运行多词才能看到效果。一般的话,带for循环的版本最多运行2~3次就会遇到线程死锁,程序无法结束。造成死锁的原因是Integer.valueOf()方法基于减少对象创建和节省内存的考虑,[-128,127]之间的数字会被缓存,当valueOf()方法传入参数在这个范围内,将直接返回缓存中的对象。也就是说,代码调用了200次Integer.valueOf()一共就返回两个不同的对象。

假如在某个线程的synchronized块之间发生了一次线程切换,那就会出现线程A等待线程B持有的Integer.valueOf(1),线程B又等待线程A持有的Integer.valueOf(2),结果大家都跑不下去了。

Jconsole: JAVA 监视和管理控制台简介

点击“检测到死锁”,查看结果如下:

Jconsole: JAVA 监视和管理控制台简介

主要参考深入理解JAVA虚拟机 jvm高级特性和最佳实践这本书,感谢作者多谢大牛~~