使用VisualVM分析性能

VisualVM 是一款免費(fèi)的蠢护,集成了多個(gè) JDK 命令行工具的可視化工具惕蹄,它能為您提供強(qiáng)大的分析能力绎狭,對 Java 應(yīng)用程序做性能分析和調(diào)優(yōu)。這些功能包括生成和分析海量數(shù)據(jù)侥涵、跟蹤內(nèi)存泄漏沼撕、監(jiān)控垃圾回收器、執(zhí)行內(nèi)存和 CPU 分析芜飘,同時(shí)它還支持在 MBeans 上進(jìn)行瀏覽和操作务豺。本文主要介紹如何使用 VisualVM 進(jìn)行性能分析及調(diào)優(yōu)。


目錄:

? 準(zhǔn)備工作

? 內(nèi)存分析篇

? ? 內(nèi)存堆Heap

? ? 永久保留區(qū)域PermGen

? CPU分析篇

? 線程分析篇

? 參考文獻(xiàn)


準(zhǔn)備工作

自從 JDK 6 Update 7 以后已經(jīng)作為 Oracle JDK 的一部分嗦明,位于 JDK 根目錄的 bin 文件夾下笼沥,無需安裝,直接運(yùn)行即可娶牌。

內(nèi)存分析篇

VisualVM 通過檢測 JVM 中加載的類和對象信息等幫助我們分析內(nèi)存使用情況敬拓,我們可以通過 VisualVM 的監(jiān)視標(biāo)簽對應(yīng)用程序進(jìn)行內(nèi)存分析。

1)內(nèi)存堆Heap

首先我們來看內(nèi)存堆Heap使用情況裙戏,我本機(jī)eclipse的進(jìn)程在visualVM顯示如下:

圖片發(fā)自簡書App


隨便寫個(gè)小程序占用內(nèi)存大的,運(yùn)行一下

程序如下:

package jvisualVM;

public class JavaHeapTest {

? ? 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, 堆內(nèi)存變大了

圖片發(fā)自簡書App


在程序運(yùn)行結(jié)束之前厕诡, 點(diǎn)擊Heap Dump 按鈕累榜, 等待一會(huì)兒,得到dump結(jié)果灵嫌,可以看到一些Summary信息

點(diǎn)擊Classes壹罚, 發(fā)現(xiàn)char[]所占用的內(nèi)存是最大的

圖片發(fā)自簡書App


雙擊它,得到如下Instances結(jié)果

圖片發(fā)自簡書App

Instances是按Size由大到小排列的

第一個(gè)就是最大的寿羞, 展開Field區(qū)域的 values

圖片發(fā)自簡書App

StringBuffer類型的 全局變量 tempOOM 占用內(nèi)存特別大猖凛, 注意局部變量是無法通過 堆dump來得到分析結(jié)果的。

另外绪穆,對于“堆 dump”來說辨泳,在遠(yuǎn)程監(jiān)控jvm的時(shí)候虱岂,VisualVM是沒有這個(gè)功能的,只有本地監(jiān)控的時(shí)候才有菠红。


2)永久保留區(qū)域PermGen

其次來看下永久保留區(qū)域PermGen使用情況

運(yùn)行一段類加載的程序第岖,代碼如下:

package jvisualVM;

import java.io.File;

import java.lang.reflect.Method;

import java.net.MalformedURLException;

import java.net.URL;

import java.net.URLClassLoader;

import java.util.ArrayList;

import java.util.List;

public class TestPermGen {

? ? private static List

? ? public static void main(String[] args) throws Exception {

? ? ? ? permLeak();

? ? }

? ? private static void permLeak() throws Exception {

? ? ? ? for (int i = 0; i < 1000; i++) {

? ? ? ? ? ? URL[] urls = getURLS();

? ? ? ? ? ? URLClassLoader urlClassloader = new URLClassLoader(urls, null);

? ? ? ? ? ? Class logfClass = Class.forName("org.apache.commons.logging.LogFactory", true,urlClassloader);

? ? ? ? ? ? Method getLog = logfClass.getMethod("getLog", String.class);

? ? ? ? ? ? Object result = getLog.invoke(logfClass, "TestPermGen");

? ? ? ? ? ? insList.add(result);

? ? ? ? ? ? System.out.println(i + ": " + result);

? ? ? ? }

? ? }

? ? private static URL[] getURLS() throws MalformedURLException {

? ? ? ? File libDir = new File("C:/Users/wadexu/.m2/repository/commons-logging/commons-logging/1.1.1");

? ? ? ? File[] subFiles = libDir.listFiles();

? ? ? ? int count = subFiles.length;

? ? ? ? URL[] urls = new URL[count];

? ? ? ? for (int i = 0; i < count; i++) {

? ? ? ? ? ? urls[i] = subFiles[i].toURI().toURL();

? ? ? ? }

? ? ? ? return urls;

? ? }

一個(gè)類型裝載之后會(huì)創(chuàng)建一個(gè)對應(yīng)的java.lang.Class實(shí)例,這個(gè)實(shí)例本身和普通對象實(shí)例一樣存儲(chǔ)于堆中试溯,我覺得之所以說是這是一種特殊的實(shí)例蔑滓,某種程度上是因?yàn)槠涑洚?dāng)了訪問PermGen區(qū)域中類型信息的代理者。

運(yùn)行一段時(shí)間后拋OutOfMemoryError了遇绞, VisualVM監(jiān)控結(jié)果如下:

圖片發(fā)自簡書App

結(jié)論:PermGen區(qū)域分配的堆空間過小键袱,我們可以通過設(shè)置-XX: PermSize參數(shù)和-XX:MaxPermSize參數(shù)來解決。

關(guān)于PermGen OOM深入分析請參考這篇文章

關(guān)于Perform GC, 請參考這篇文章

這部分知識還是比較深入的摹闽,有空還要繼續(xù)研究蹄咖。


CPU分析篇

CPU 性能分析的主要目的是統(tǒng)計(jì)函數(shù)的調(diào)用情況及執(zhí)行時(shí)間,或者更簡單的情況就是統(tǒng)計(jì)應(yīng)用程序的 CPU 使用情況钩骇。

沒有程序運(yùn)行時(shí)的 CPU 使用情況如下圖:

圖片發(fā)自簡書App

運(yùn)行一段 占用CPU 的小程序比藻,代碼如下

package jvisualVM;

public class MemoryCpuTest {

? ? public static void main(String[] args) throws InterruptedException {

? ? ? ? cpuFix();

? ? }

? ? /**

? ? * cpu 運(yùn)行固定百分比

? ? *

* @throws InterruptedException

*/

public static void cpuFix() throws InterruptedException {

// 80%的占有率

int busyTime = 8;

// 20%的占有率

int idelTime = 2;

// 開始時(shí)間

long startTime = 0;

while (true) {

// 開始時(shí)間

startTime = System.currentTimeMillis();

/*

* 運(yùn)行時(shí)間

*/

while (System.currentTimeMillis() - startTime < busyTime) {

;

}

// 休息時(shí)間

Thread.sleep(idelTime);

}

}

}

查看監(jiān)視頁面 Monitor tab

圖片發(fā)自簡書App

過高的 CPU 使用率可能是由于我們的項(xiàng)目中存在低效的代碼;

在我們對程序施壓的時(shí)候倘屹,過低的 CPU 使用率也有可能是程序的問題银亲。

點(diǎn)擊取樣器Sampler, 點(diǎn)擊“CPU”按鈕纽匙, 啟動(dòng)CPU性能分析會(huì)話务蝠,VisualVM 會(huì)檢測應(yīng)用程序所有的被調(diào)用的方法,

在CPU samples tab 下可以看到我們的方法cpufix() 的自用時(shí)間最長烛缔, 如下圖:

圖片發(fā)自簡書App

切換到Thread CPU Time 頁面下馏段,我們的 main 函數(shù)這個(gè)進(jìn)程 占用CPU時(shí)間最長, 如下圖:

圖片發(fā)自簡書App


線程分析篇

Java 語言能夠很好的實(shí)現(xiàn)多線程應(yīng)用程序践瓷。當(dāng)我們對一個(gè)多線程應(yīng)用程序進(jìn)行調(diào)試或者開發(fā)后期做性能調(diào)優(yōu)的時(shí)候院喜,往往需要了解當(dāng)前程序中所有線程的運(yùn)行狀態(tài),是否有死鎖晕翠、熱鎖等情況的發(fā)生喷舀,從而分析系統(tǒng)可能存在的問題。

在 VisualVM 的監(jiān)視標(biāo)簽內(nèi)淋肾,我們可以查看當(dāng)前應(yīng)用程序中所有活動(dòng)線程(Live threads)和守護(hù)線程(Daemon threads)的數(shù)量等實(shí)時(shí)信息硫麻。

運(yùn)行一段小程序,代碼如下:

package jvisualVM;

public class MyThread extends Thread{

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增加兩個(gè) 變成13了

Daemon threads從8增加兩個(gè) 變成10了

圖片發(fā)自簡書App

VisualVM 的線程標(biāo)簽提供了三種視圖樊卓,默認(rèn)會(huì)以時(shí)間線的方式展現(xiàn)拿愧, 如下圖:

圖片發(fā)自簡書App


可以看到兩個(gè)我們r(jià)un的程序里啟的線程:My-Thread-1 和 My-Thread-2

圖片發(fā)自簡書App

另外還有兩種視圖分別是表視圖和詳細(xì)信息視圖, 這里看一下每個(gè)Thread的詳細(xì)視圖:

圖片發(fā)自簡書App


再來一段死鎖的程序碌尔,看VisualVM 能否分析出來

package jvisualVM;

public class DeadLock {

? ? public static void main(String[] args) {

? ? ? ? Resource r1 = new Resource();

? ? ? ? Resource r0 = new Resource();

? ? ? ? Thread myTh1 = new LockThread1(r1, r0);

? ? ? ? Thread myTh0 = new LockThread0(r1, r0);

? ? ? ? myTh1.setName("DeadLock-1 ");

? ? ? ? myTh0.setName("DeadLock-0 ");

? ? ? ? myTh1.start();

? ? ? ? myTh0.start();

? ? }

}

? ? class Resource {

? ? ? ? private int i;

public int getI() {

return i;

}

public void setI(int i) {

this.i = i;

}

}

? ? class LockThread1 extends Thread {

? ? ? ? private Resource r1, r2;

public LockThread1(Resource r1, Resource r2) {

this.r1 = r1;

this.r2 = r2;

}

@Override

public void run() {

int j = 0;

while (true) {

synchronized (r1) {

System.out.println("The first thread got r1's lock " + j);

synchronized (r2) {

System.out.println("The first thread got r2's lock? " + j);

}

}

j++;

}

}

}

? ? class LockThread0 extends Thread {

? ? ? ? private Resource r1, r2;

public LockThread0(Resource r1, Resource r2) {

this.r1 = r1;

this.r2 = r2;

}

@Override

public void run() {

int j = 0;

while (true) {

synchronized (r2) {

System.out.println("The second thread got r2's lock? " + j);

synchronized (r1) {

System.out.println("The second thread got r1's lock" + j);

}

}

j++;

}

}

}

打開VisualVM檢測到的JVM進(jìn)程,我們可以看到這個(gè)tab在閃,VisualVM已經(jīng)檢測到我這個(gè)package下面的DeadLock類出錯(cuò)了

切換到Thread tab铣卡, 可以看到死鎖了, Deadlock detected!

另外可以點(diǎn)擊Thread Dump 線程轉(zhuǎn)儲(chǔ)陪白,進(jìn)一步分析,在這里就不贅述了膳灶,有興趣的讀者可以自行實(shí)驗(yàn)咱士。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市轧钓,隨后出現(xiàn)的幾起案子序厉,更是在濱河造成了極大的恐慌,老刑警劉巖毕箍,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弛房,死亡現(xiàn)場離奇詭異,居然都是意外死亡而柑,警方通過查閱死者的電腦和手機(jī)文捶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來媒咳,“玉大人粹排,你說我怎么就攤上這事∩瑁” “怎么了顽耳?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長妙同。 經(jīng)常有香客問我射富,道長,這世上最難降的妖魔是什么粥帚? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任胰耗,我火速辦了婚禮,結(jié)果婚禮上芒涡,老公的妹妹穿的比我還像新娘宪郊。我一直安慰自己,他們只是感情好拖陆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著懊亡,像睡著了一般依啰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上店枣,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天速警,我揣著相機(jī)與錄音叹誉,去河邊找鬼。 笑死闷旧,一個(gè)胖子當(dāng)著我的面吹牛长豁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播忙灼,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼匠襟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了该园?” 一聲冷哼從身側(cè)響起酸舍,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎里初,沒想到半個(gè)月后啃勉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡双妨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年淮阐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刁品。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泣特,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出哑诊,到底是詐尸還是另有隱情群扶,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布镀裤,位于F島的核電站竞阐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏暑劝。R本人自食惡果不足惜骆莹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望担猛。 院中可真熱鬧幕垦,春花似錦、人聲如沸傅联。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蒸走。三九已至仇奶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間比驻,已是汗流浹背该溯。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工岛抄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人狈茉。 一個(gè)月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓夫椭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親氯庆。 傳聞我的和親對象是個(gè)殘疾皇子蹭秋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法点晴,內(nèi)部類的語法感凤,繼承相關(guān)的語法,異常的語法粒督,線程的語...
    子非魚_t_閱讀 31,631評論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理陪竿,服務(wù)發(fā)現(xiàn),斷路器屠橄,智...
    卡卡羅2017閱讀 134,656評論 18 139
  • 從三月份找實(shí)習(xí)到現(xiàn)在族跛,面了一些公司,掛了不少锐墙,但最終還是拿到小米礁哄、百度、阿里溪北、京東桐绒、新浪、CVTE之拨、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,246評論 11 349
  • 0×1.ACL概述ACL(Access Control List茉继,訪問控制列表),是一系列運(yùn)用到路由器接口的指令列...
    Zero___閱讀 2,718評論 0 3
  • PS基礎(chǔ)知識小科普睬魂,么么噠终吼! 提到PS的畫筆預(yù)設(shè),很多小伙伴都會(huì)很快想到Brush的諸多技能點(diǎn)氯哮,尤其是豐富的abr...
    阿隨向前沖閱讀 4,380評論 4 25