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

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

启动JConsole

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

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

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

内存监控

相当于可视化的jstat命令,用于监视受收集器管理的虚拟机内存(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线程,如下所示:

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

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

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

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),结果大家都跑不下去了。


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

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

posted @ 2016-03-27 22:10  smile_tina  阅读(3163)  评论(1编辑  收藏  举报