JConsole(java monitoring and management console)
1. 介绍
位置:jdk\bin
使用:双击exe文件打开控制台
作用:JConsole是一款基于JMX的可视化监视和管理工具。
JMX,即Java管理扩展(Java Management Extensions),是一种用于管理和监控Java应用程序与系统资源的技术。它允许开发人员和管理员以标准化的方式访问和操作应用程序内部的状态信息、配置参数、统计指标等,从而实现对应用程序的监控、配置和管理功能。
通过JMX,可以实现如下功能:
监控应用状态:比如内存使用情况、线程状态、CPU负载等。
配置动态修改:在不重启应用程序的情况下调整配置参数。
性能管理:收集并分析性能数据,帮助优化应用性能。
通知与警报:当特定事件发生或阈值被越过时,发送通知或警报。
远程管理**:通过网络远程监控和管理运行在另一台机器上的Java应用。
JMX的核心概念包括Managed Beans (MBeans),它们代表了可管理的资源;MBean Server,作为注册和访问MBeans的中心;以及**代理(Agent)**结构,它整合了MBean Server和其他服务(如连接器、适配器),使得远程管理成为可能。JMX广泛应用于Java应用的运维和管理中,是Java平台标准的一部分。
JConsole 基本包括以下基本功能:概述、内存、线程、类、VM概要、MBean
2. 内存监控
运行下面的程序、然后使用JConsole进行监控;注意设置虚拟机参数
public class JConsoleDemo {
static class OOMObject {
public byte[] placeholder = new byte[64 * 1024];
}
public static void fillHeap(int num) throws InterruptedException {
Thread.sleep(20000); // 先运行程序,在执行监控
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);
while (true) {
//让其一直运行着
}
}
}
“内存”页签相当于可视化的jstat 命令,用于监视受收集器管理的虚拟机内存的变换趋势。
3. 线程监控
如果上面的“内存”页签相当于可视化的jstat命令的话,“线程”页签的功能相当于可视化的jstack命令,遇到线程停顿时可以使用这个页签进行监控分析。线程长时间停顿的主要原因主要有:等待外部资源(数据库连接、网络资源、设备资源等)、死循环、锁等待(活锁和死锁)
下面三个方法分别等待控制台输入、死循环演示、线程锁等待演示
- 第一步:运行如下代码:
package com.xin.demo.threaddemo.bookdemo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class JConsoleDemo1 {
public static void main(String[] args) throws IOException {
waitRerouceConnection();
createBusyThread();
createLockThread(new Object());
}
/**
* 等待控制台输入
*
* @throws IOException
*/
public static void waitRerouceConnection() throws IOException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try {
br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
}
}, "waitRerouceConnection");
thread.start();
}
/**
* 线程死循环演示
*/
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();
}
}
-
第二步:打开jconsole中查看上面程序运行情况,可以查看到3个目标线程
-
第三步:查看目标线程信息
-
waitRerouceConnection线程处于读取数据状态,如下图:
-
testBusyThread线程位于代码39行,处于运行状态,如下图:
-
testLockThread处于活锁等待状态,如下图:
只要lock对象的notify()或notifyAll()方法被调用,这个线程便可能激活以继续执行
-
通过“线程”这个窗口可以很方便查询虚拟机中的线程堆栈信息,对发现系统中的一些问题非常有帮助。
4. 线程死锁演示
- 第一步:运行下面代码:
package com.xin.demo.threaddemo.bookdemo;
public class JConsoleDemo2 {
public static void main(String[] args) {
User u1 = new User("u1");
User u2 = new User("u2");
Thread thread1 = new Thread(new SynAddRunalbe(u1, u2, 1, 2, true));
thread1.setName("thread1");
thread1.start();
Thread thread2 = new Thread(new SynAddRunalbe(u1, u2, 2, 1, false));
thread2.setName("thread2");
thread2.start();
}
/**
* 线程死锁等待演示
*/
public static class SynAddRunalbe implements Runnable {
User u1, u2;
int a, b;
boolean flag;
public SynAddRunalbe(User u1, User u2, int a, int b, boolean flag) {
this.u1 = u1;
this.u2 = u2;
this.a = a;
this.b = b;
this.flag = flag;
}
@Override
public void run() {
try {
if (flag) {
synchronized (u1) {
Thread.sleep(100);
synchronized (u2) {
System.out.println(a + b);
}
}
} else {
synchronized (u2) {
Thread.sleep(100);
synchronized (u1) {
System.out.println(a + b);
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
}
thread1持有u1的锁,thread2持有u2的锁,thread1等待获取u2的锁,thread2等待获取u1的锁,相互需要获取的锁都被对方持有者,造成了死锁。程序中出现了死锁的情况,我们是比较难以发现的。需要依靠工具解决。刚好jconsole就是这个美妙的工具。
- 第二步:在jconsole中打开上面程序的监控信息:
从上面可以看出代码42行和35行处导致了死锁。
关于程序死锁的,我们还可以使用命令行工具jstack来查看java线程堆栈信息,也可以发现死锁。