jconsole Java監視與管理控制臺
1、jconsole介紹
jconsole
(java monitoring and management console
)是一款基于JMX (Java Management Extensions
)
的可視化監視和管理工具。
2、啟動jconsole
1、在linux
和windwos
下通過jconsole
啟動即可。
2、然后會自動搜索本機運行的所有虛擬機進程。
3、選擇其中一個進程可開始進行監控。
3、jconsole基本介紹
jconsole
基本包括以下基本功能:概述
、內存
、線程
、類
、VM概要
、MBean
。
運行下面的程序,然后使用jconsole進行監控,注意設置虛擬機參數。
package com.example.controller;import java.util.ArrayList;
import java.util.List;public class Demo1 {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) {//讓其一直運行著}}
}
配置啟動參數:-Xms100M -XX:+UseSerialGC -XX:+PrintGCDetails
可以切換頂部的選項卡查看各種指標信息:
內存監控
內存
頁簽相當于可視化的jstat
命令,用于監視受收集器管理的虛擬機內存的變換趨勢。
代碼運行,控制臺也會輸出gc日志:
[GC (Allocation Failure) [DefNew: 27305K->3392K(30720K), 0.0087378 secs] 27305K->14929K(99008K), 0.0088041 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [DefNew: 30720K->3369K(30720K), 0.0125603 secs] 42257K->38591K(99008K), 0.0125827 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [DefNew: 30697K->3367K(30720K), 0.0206170 secs] 65919K->63766K(99008K), 0.0206669 secs] [Times: user=0.00 sys=0.02, real=0.02 secs]
[Full GC (System.gc()) [Tenured: 60398K->66528K(68288K), 0.0339940 secs] 66709K->66528K(99008K), [Metaspace: 9255K->9255K(1058816K)], 0.0343586 secs] [Times: user=0.05 sys=0.00, real=0.04 secs]
4、線程監控
如果上面的內存頁簽相當于可視化的jstat命令的話,線程頁簽的功能相當于可視化的jstack命令,遇到線程停頓
時可以使用這個頁簽進行監控分析。線程長時間停頓的主要原因主要有:等待外部資源(數據庫連接、網絡資源、
設備資源等)、死循環、鎖等待(活鎖和死鎖)
下面三個方法分別等待控制臺輸入、死循環演示、線程鎖等待演示。
**第一步:**運行下面的代碼。
package com.example.controller;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;public class Demo2 {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() {@Overridepublic 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() {@Overridepublic void run() {while (true) {;}}}, "testBusyThread");thread.start();}/*** 線程鎖等待演示*/public static void createLockThread(final Object lock) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {synchronized (lock) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}, "testLockThread");thread.start();}
}
**第二步:**打開jconsole中查看上面程序運行情況,可以查看到3個目標線程。
**第三步:**查看目標線程信息
waitRerouceConnection線程處于讀取數據狀態,如下圖:
testBusyThread線程位于代碼45行,處于運行狀態,如下圖:
testLockThread處于活鎖等待狀態,如下圖:
只要lock對象的notify()或notifyAll()方法被調用,這個線程便可能激活以繼續執行。
通過 線程
這個窗口可以很方便查詢虛擬機中的線程堆棧信息,對發現系統中的一些問題非常有幫助。
5、線程死鎖演示
**第一步:**運行下面代碼:
package com.example.controller;public class Demo3 {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;}@Overridepublic 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;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +'}';}}
}
thread1持有u1的鎖,thread2持有u2的鎖,thread1等待獲取u2的鎖,thread2等待獲取u1的鎖,相互需要獲取
的鎖都被對方持有者,造成了死鎖。程序中出現了死鎖的情況,我們是比較難以發現的。需要依靠工具解決。剛好
jconsole就是這個美妙的工具。
**第二步:**在jconsole中打開上面程序的監控信息:
從上面可以看出代碼39行和46行處導致了死鎖。
關于程序死鎖的,我們還可以使用命令行工具jstack來查看java線程堆棧信息,也可以發現死鎖。