本地程序(相對於開啓JConsole的計算機),無需設置任何參數就能夠被本地開啓的JConsole鏈接(Java SE 6開始無需設置,以前仍是須要設置運行時參數 -Dcom.sun.management.jmxremote )
無認證鏈接 (下面的設置表示:鏈接的端口爲899九、無需認證就能夠被鏈接)
html
-Dcom.sun.management.jmxremote.port=8999 \ -Dcom.sun.management.jmxremote.authenticate=false \ -Dcom.sun.management.jmxremote.ssl=false連接另外一臺計算機
jconsole.exe 192.168.0.181:8999
下面使用內存、線程進行案例分析java
public class JconsoleHeapTest { public static final int _1MB = 1024 * 1024; public static void main(String[] args) { try { Thread.sleep(5000); }catch (Exception e){ } System.out.println("method start ====>"); fill(1000); System.out.println("<==== method end "); } private static void fill(Integer cnt){ List<JconsoleHeapTest> jconsoleTests = new ArrayList<>(); for(int i = 0; i<cnt; i++){ try { Thread.sleep(100); }catch (Exception e){ } jconsoleTests.add(new JconsoleHeapTest()); } } }運行程序,創建連接
public class JconsoleThreadTest { public static final int _1MB = 1024 * 1024; public static void main(String[] args) { Scanner scanner = new Scanner(System.in); scanner.next(); new Thread(() -> { System.out.println("start whileThread ===>"); while (true){ } }, "whileThread").start(); scanner.next(); new Thread(new Runnable() { @Override public void run() { System.out.println("start watiThread ===>"); Object o = new Object(); synchronized (o){ try { o.wait(); }catch (Exception e){ } } } }, "watiThread").start(); } }創建連接後,首先觀察main線程,一直在運行狀態
public class JconsoleSyncDeadLockTest { private static Object locka = new Object(); private static Object lockb = new Object(); public static void main(String[] args){ new JconsoleSyncDeadLockTest().deadLock(); } private void deadLock(){ Thread thread1 = new Thread(new Runnable() { @Override public void run() { synchronized (locka){ try{ System.out.println(Thread.currentThread().getName()+" get locka ing!"); Thread.sleep(500); System.out.println(Thread.currentThread().getName()+" after sleep 500ms!"); }catch(Exception e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" need lockb!Just waiting!"); synchronized (lockb){ System.out.println(Thread.currentThread().getName()+" get lockb ing!"); } } } },"thread1"); Thread thread2 = new Thread(new Runnable() { @Override public void run() { synchronized (lockb){ try{ System.out.println(Thread.currentThread().getName()+" get lockb ing!"); Thread.sleep(500); System.out.println(Thread.currentThread().getName()+" after sleep 500ms!"); }catch(Exception e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" need locka! Just waiting!"); synchronized (locka){ System.out.println(Thread.currentThread().getName()+" get locka ing!"); } } } },"thread2"); thread1.start(); thread2.start(); } }運行後,兩個線程相互等待,發生死鎖
public final static int OUTOFMEMORY = 200000000; private String oom; private int length; StringBuffer tempOOM = new StringBuffer(); public JavaHeapTest(int leng) { this.length = leng; int i = 0; while (i < leng) { i++; try { tempOOM.append("a"); } catch (OutOfMemoryError e) { e.printStackTrace(); break; } } this.oom = tempOOM.toString(); } public String getOom() { return oom; } public int getLength() { return length; } public static void main(String[] args) { JavaHeapTest javaHeapTest = new JavaHeapTest(OUTOFMEMORY); System.out.println(javaHeapTest.getOom().length()); }
查看VisualVM Monitor tab, 堆內存變大了shell
在程序運行結束以前, 點擊Heap Dump 按鈕, 等待一下子,獲得dump結果,能夠看到一些Summary信息多線程
點擊Classes, 發現char[]所佔用的內存是最大的
app
雙擊它,獲得以下Instances結果jvm
Instances是按Size由大到小排列的ide
第一個就是最大的, 展開Field區域的 values函數
另外,對於「堆 dump」來講,在遠程監控jvm的時候,VisualVM是沒有這個功能的,只有本地監控的時候纔有。
工具
Java 語言可以很好的實現多線程應用程序。當咱們對一個多線程應用程序進行調試或者開發後期作性能調優的時候,每每須要瞭解當前程序中全部線程的運行狀態,是否有死鎖、熱鎖等狀況的發生,從而分析系統可能存在的問題。性能
在 VisualVM 的監視標籤內,咱們能夠查看當前應用程序中全部活動線程(Live threads)和守護線程(Daemon threads)的數量等實時信息。
public static void main(String[] args) { MyThread mt1 = new MyThread("Thread a"); MyThread mt2 = new MyThread("Thread b"); mt1.setName("My-Thread-1 "); mt2.setName("My-Thread-2 "); mt1.start(); mt2.start(); } public MyThread(String name) { } public void run() { while (true) { } }
Live threads 從11增長兩個 變成13了
Daemon threads從8增長兩個 變成10了
VisualVM 的線程標籤提供了三種視圖,默認會以時間線的方式展示, 以下圖:
能夠看到兩個咱們run的程序裏啓的線程:My-Thread-1 和 My-Thread-2
另外還有兩種視圖分別是表視圖和詳細信息視圖, 這裏看一下每一個Thread的詳細視圖:
public static void main(String[] args) throws InterruptedException {
cpuFix(); }
/** * cpu 運行固定百分比 * * @throws InterruptedException */ public static void cpuFix() throws InterruptedException {
// 80%的佔有率 int busyTime = 8; // 20%的佔有率 int idelTime = 2; // 開始時間 long startTime = 0; while (true) {
// 開始時間 startTime = System.currentTimeMillis(); /* * 運行時間 */ while (System.currentTimeMillis() - startTime < busyTime) {
; }
// 休息時間 Thread.sleep(idelTime); }
}
查看監視頁面 Monitor tab
太高的 CPU 使用率多是因爲咱們的項目中存在低效的代碼;
在咱們對程序施壓的時候,太低的 CPU 使用率也有多是程序的問題。
點擊取樣器Sampler, 點擊「CPU」按鈕, 啓動CPU性能分析會話,VisualVM 會檢測應用程序全部的被調用的方法,
在CPU samples tab 下能夠看到咱們的方法cpufix() 的自用時間最長, 以下圖:
切換到Thread CPU Time 頁面下,咱們的 main 函數這個進程 佔用CPU時間最長, 以下圖: