深入理解JVM虛擬機(jī)12:JVM性能管理神器VisualVM介紹與實(shí)戰(zhàn)

本文轉(zhuǎn)自互聯(lián)網(wǎng),侵刪

本系列文章將整理到我在GitHub上的《Java面試指南》倉(cāng)庫(kù),更多精彩內(nèi)容請(qǐng)到我的倉(cāng)庫(kù)里查看

https://github.com/h2pl/Java-Tutorial

喜歡的話麻煩點(diǎn)下Star哈

文章將同步到我的個(gè)人博客:

www.how2playlife.com

本文是微信公眾號(hào)【Java技術(shù)江湖】的《深入理解JVM虛擬機(jī)》其中一篇,本文部分內(nèi)容來(lái)源于網(wǎng)絡(luò),為了把本文主題講得清晰透徹,也整合了很多我認(rèn)為不錯(cuò)的技術(shù)博客內(nèi)容,引用其中了一些比較好的博客文章,如有侵權(quán),請(qǐng)聯(lián)系作者。

該系列博文會(huì)告訴你如何從入門(mén)到進(jìn)階,一步步地學(xué)習(xí)JVM基礎(chǔ)知識(shí),并上手進(jìn)行JVM調(diào)優(yōu)實(shí)戰(zhàn),JVM是每一個(gè)Java工程師必須要學(xué)習(xí)和理解的知識(shí)點(diǎn),你必須要掌握其實(shí)現(xiàn)原理,才能更完整地了解整個(gè)Java技術(shù)體系,形成自己的知識(shí)框架。

為了更好地總結(jié)和檢驗(yàn)?zāi)愕膶W(xué)習(xí)成果,本系列文章也會(huì)提供每個(gè)知識(shí)點(diǎn)對(duì)應(yīng)的面試題以及參考答案。

如果對(duì)本系列文章有什么建議,或者是有什么疑問(wèn)的話,也可以關(guān)注公眾號(hào)【Java技術(shù)江湖】聯(lián)系作者,歡迎你參與本系列博文的創(chuàng)作和修訂。

一、VisualVM是什么?

VisualVM是一款免費(fèi)的JAVA虛擬機(jī)圖形化監(jiān)控分析工具。



1.  擁有圖形化的監(jiān)控界面。
2. 提供本地、遠(yuǎn)程的JVM監(jiān)控分析功能。
3. 是一款免費(fèi)的JAVA工具。
4. VisualVM擁有豐富的插件支持。

二、如何獲取VisualVM?

VisualVM官方網(wǎng)站:http://visualvm.java.net/

VisualVM各版本下載頁(yè)面: http://visualvm.java.net/releases.html

 下載VisualVM時(shí)也應(yīng)該注意,不同的JDK版本對(duì)應(yīng)不同版本的VisualVM,具體根據(jù)安裝的JDK版本來(lái)下載第一的VisualVM。

三、獲取那個(gè)版本?

   下載版本參考:Java虛擬機(jī)性能管理神器 - VisualVM(4) - JDK版本與VisualVM版本對(duì)應(yīng)關(guān)系

備注:下列表中顯示1.3.6版本只適合JDK7和JDK8,可是我用1.3.6版還是可以監(jiān)控JDK1.6_45的版本。

四、VisualVM能做什么?

  1. 顯示JAVA應(yīng)用程序配置和運(yùn)行時(shí)環(huán)境。
    顯示JAVA應(yīng)用程序JVM參數(shù),系統(tǒng)屬性,JVM的信息和運(yùn)行環(huán)境。
  1. 顯示本地和遠(yuǎn)程JAVA應(yīng)用程序運(yùn)行狀態(tài)。
    可以連接到遠(yuǎn)程服務(wù)器上運(yùn)行的JAVA應(yīng)用程序,監(jiān)控應(yīng)用程序的運(yùn)行狀態(tài)。

  2. 監(jiān)控應(yīng)用程序的性能消耗。
    可以監(jiān)控到應(yīng)用程序熱點(diǎn)方法的執(zhí)行單次時(shí)間、總耗時(shí)、耗時(shí)占比。

  1. 顯示應(yīng)用程序內(nèi)存分配,顯示分析堆信息。
    顯示應(yīng)用程序在運(yùn)行時(shí)的編譯時(shí)間、加載時(shí)間、垃圾回收時(shí)間、內(nèi)存區(qū)域的回收狀態(tài)等。
  1. 監(jiān)控應(yīng)用程序線程狀態(tài)和生命周期。
    監(jiān)控應(yīng)用程序線程的運(yùn)行、休眠、等待、鎖定狀態(tài)。
  1. 顯示、分析線程堆信息。
    顯示線程當(dāng)前運(yùn)行狀態(tài)和關(guān)聯(lián)類信息。
  1. 支持第三方插件來(lái)分析JAVA應(yīng)用程序。
    另外還提供更多更強(qiáng)大、方便的第三方插件。

監(jiān)控遠(yuǎn)程主機(jī)上的JAVA應(yīng)用程序

使用VisualVM監(jiān)控遠(yuǎn)程主機(jī)上JAVA應(yīng)用程序時(shí),需要開(kāi)啟遠(yuǎn)程主機(jī)上的遠(yuǎn)程監(jiān)控訪問(wèn),或者在遠(yuǎn)程JAVA應(yīng)用程序啟動(dòng)時(shí),開(kāi)啟遠(yuǎn)程監(jiān)控選項(xiàng),兩種方法,選擇其中一種就可以開(kāi)啟遠(yuǎn)程監(jiān)控功能,配置完成后就可以在本地對(duì)遠(yuǎn)程主機(jī)上的JAVA應(yīng)用程序進(jìn)行監(jiān)控。

1.遠(yuǎn)程服務(wù)器、應(yīng)用程序配置
1.1配合jstatd工具提供監(jiān)控?cái)?shù)據(jù)
1.1.1創(chuàng)建安全訪問(wèn)文件
在JAVA_HOME/bin目錄中,創(chuàng)建名稱為jstatdAllPolicy文件(這個(gè)文件名稱也可以順便起,不過(guò)要與jstatd啟動(dòng)時(shí)指定名稱相同),將以下內(nèi)容拷貝到文件中。并保證文件的權(quán)限和用戶都正確。

    grant codebase"file:${java.home}/../lib/tools.jar"{ permission java.security.AllPermission; };

1.1.2啟動(dòng)jstatd服務(wù)
在JAVA_HOME/bin目錄中,執(zhí)行以下命令:

     ./jstatd -J-Djava.security.policy=jstatdAllPolicy-p 1099 -J-Djava.rmi.server.hostname=192.168.xxx.xxx



    jstatd命令描述以及參數(shù)說(shuō)明:

       jstatd是一個(gè)基于RMI(Remove Method Invocation)的服務(wù)程序,它用于監(jiān)控基于HotSpot的JVM中資源的創(chuàng)建及銷毀,并且提供了一個(gè)遠(yuǎn)程接口允許遠(yuǎn)程的監(jiān)控工具連接到本地的JVM執(zhí)行命令。



    -J-Djava.security.policy=jstatdAllPolicy 指定安全策略文件名稱

     -p 1099  指定啟動(dòng)端口

     -J-Djava.rmi.server.hostname=192.168.xxx.xxx  指定本機(jī)IP地址,在hosts文件配置不正常時(shí)使用,最好加上。

1.2JVM啟動(dòng)時(shí)配置遠(yuǎn)程監(jiān)控選項(xiàng)
在需要遠(yuǎn)程監(jiān)控的JVM啟動(dòng)時(shí),開(kāi)啟遠(yuǎn)程監(jiān)控選項(xiàng)

    -Dcom.sun.management.jmxremote.port=1099
    -Dcom.sun.management.jmxremote.ssl=false
    -Dcom.sun.management.jmxremote.authenticate=false
    -Djava.rmi.server.hostname=192.168.xxx.xxx

2.本地VisualVM配置
在本地VisualVM的應(yīng)用程序窗口,右鍵單擊【遠(yuǎn)程】》【添加遠(yuǎn)程主機(jī)】》【主機(jī)名】中輸入遠(yuǎn)程主機(jī)的IP地址,點(diǎn)擊【高級(jí)設(shè)置】輸入遠(yuǎn)程主機(jī)開(kāi)啟的監(jiān)控端口,點(diǎn)擊【確定】完成配置。

    如果一切正常,就可以看到遠(yuǎn)程主機(jī)上的JAVA應(yīng)用程序了。

排查JAVA應(yīng)用程序內(nèi)存泄漏

  1. 發(fā)現(xiàn)問(wèn)題
    線上應(yīng)用部署完成后,運(yùn)行12天左右就會(huì)出現(xiàn)假死,或者某天早上810點(diǎn)高峰期間突然不處理數(shù)據(jù)了。由于在測(cè)試環(huán)境的壓力測(cè)試沒(méi)有做完全,也沒(méi)有遇到相關(guān)問(wèn)題。情況出現(xiàn)后對(duì)客戶的使用造成很大影響,領(lǐng)導(dǎo)要求趕緊排查出問(wèn)題原因!

  2. 排查原因
    排查原因前,與運(yùn)維溝通,了解線上服務(wù)器的運(yùn)行狀態(tài),通過(guò)ganglila觀察網(wǎng)絡(luò)、CPU、內(nèi)存、磁盤(pán)的運(yùn)行歷史狀態(tài),發(fā)現(xiàn)程序故障前,都有一波很高的負(fù)載,排查線上日志,負(fù)載來(lái)源在8~9點(diǎn)平臺(tái)接入數(shù)據(jù)量成倍增加,通過(guò)與產(chǎn)品和市場(chǎng)人員分析,此時(shí)段是用戶集中上班、接入平臺(tái)的高峰時(shí)段,訪問(wèn)日志也顯示,業(yè)務(wù)場(chǎng)景正常,無(wú)網(wǎng)絡(luò)攻擊和安全問(wèn)題。屬于產(chǎn)品業(yè)務(wù)正常的場(chǎng)景。

     排除了網(wǎng)絡(luò)安全因素后,就從程序的運(yùn)行內(nèi)部進(jìn)行排查,首先想到的獲取JVM的dmp文件。獲取JVM的dmp文件有兩中方式:
    
     1. JVM啟動(dòng)時(shí)增加兩個(gè)參數(shù),出現(xiàn) OOME 時(shí)生成堆 dump: 
    
             -XX:+HeapDumpOnOutOfMemoryError
    
             生成堆文件地址:
    
             -XX:HeapDumpPath=/home/test/jvmlogs/ 
    
     2. 發(fā)現(xiàn)程序異常前通過(guò)執(zhí)行指令,直接生成當(dāng)前JVM的dmp文件,15434是指JVM的進(jìn)程號(hào)
    
             jmap -dump:format=b,file=serviceDump.dat    15434 
    
     由于第一種方式是一種事后方式,需要等待當(dāng)前JVM出現(xiàn)問(wèn)題后才能生成dmp文件,實(shí)時(shí)性不高,第二種方式在執(zhí)行時(shí),JVM是暫停服務(wù)的,所以對(duì)線上的運(yùn)行會(huì)產(chǎn)生影響。所以建議第一種方式。
    
  3. 解決方案
    獲取到dmp文件后,就開(kāi)始進(jìn)行分析。將服務(wù)器上的dmp文件拷貝到本地,然后啟動(dòng)本地的VisualVM,點(diǎn)擊菜單欄【文件】選項(xiàng),裝入dmp文件

    打開(kāi)dmp文件后,查看類標(biāo)簽,就能看到占用內(nèi)存的一個(gè)排行。



    然后通過(guò)檢查中查找最大的對(duì)象,排查到具體線程和對(duì)象。





    上列中的com.ctfo.trackservice.handler.TrackHandleThread#4就是重點(diǎn)排查對(duì)象。

    通過(guò)代碼的比對(duì),在此線程中,有調(diào)用DAO接口,負(fù)責(zé)將數(shù)據(jù)存儲(chǔ)到數(shù)據(jù)庫(kù)中。而存儲(chǔ)到數(shù)據(jù)庫(kù)中時(shí),由于存儲(chǔ)速度較慢,導(dǎo)致此線程中的數(shù)據(jù)隊(duì)列滿了,數(shù)據(jù)積壓,無(wú)法回收導(dǎo)致了隊(duì)列鎖定,結(jié)果就是程序假死,不處理數(shù)據(jù)。



    通過(guò)進(jìn)一步分析,發(fā)現(xiàn)數(shù)據(jù)庫(kù)存儲(chǔ)時(shí)有瓶頸,雖然當(dāng)前是批量提交,速度也不快。平均8000/秒的存儲(chǔ)速度。而數(shù)據(jù)庫(kù)有一個(gè)DG(備份)節(jié)點(diǎn),采用的是同步備份方式,即主庫(kù)事務(wù)要等DG的事務(wù)也完成后才能返回成功,這樣就會(huì)因?yàn)榫W(wǎng)絡(luò)因素、DG性能因素等原因?qū)е滦阅芟陆怠Mㄟ^(guò)與DBA、產(chǎn)品、溝通,將同步備份改為異步備份,實(shí)時(shí)同步改為異步(異步可能會(huì)導(dǎo)致主備有10分鐘以內(nèi)的數(shù)據(jù)延遲)。速度達(dá)到30000/秒。問(wèn)題解決。

    至此,通過(guò)VisualVM分析java程序內(nèi)存泄漏到此結(jié)束。不過(guò)還有幾個(gè)問(wèn)題:1. 如果dmp文件較大,VisualVM分析時(shí)間可能很久;另外,VisualVM對(duì)堆的分析顯示功能還不算全面。如果需要更全面的顯示,就可以使用另外一個(gè)專業(yè)的dmp文件分析工具【Memory Analyzer (MAT)】,此工具可以作為eclipse的插件進(jìn)行安裝,也可以單獨(dú)下載使用。如果有感興趣的朋友,我個(gè)人建議還是單獨(dú)下載使用。下載地址:http://www.eclipse.org/mat/   

查找JAVA應(yīng)用程序耗時(shí)的方法函數(shù)

1.為什么要監(jiān)控?
JAVA程序在開(kāi)發(fā)前,根據(jù)設(shè)計(jì)文檔的性能需求,是要對(duì)程序的性能指標(biāo)進(jìn)行測(cè)試的。比如接口每秒響應(yīng)次數(shù)要求1000次/秒,就需要平均每次請(qǐng)求處理的時(shí)間在1ms以內(nèi),如果需要滿足這個(gè)指標(biāo),就需要在開(kāi)發(fā)階段對(duì)接口執(zhí)行函數(shù)進(jìn)行監(jiān)控,也可以通過(guò)打印日志進(jìn)行監(jiān)控,從而統(tǒng)計(jì)對(duì)應(yīng)的性能指標(biāo),然后可以根據(jù)性能指標(biāo)的要求進(jìn)行相應(yīng)優(yōu)化。

  1. 那些方法函數(shù)需要監(jiān)控?
    根據(jù)具體業(yè)務(wù)的場(chǎng)景和需求,主要集中在IO通訊、文件讀寫(xiě)、數(shù)據(jù)庫(kù)操作、業(yè)務(wù)邏輯處理上,這些都是制約性能的重要因素,所以需要重點(diǎn)關(guān)注。
  1. 如何排查
    在研發(fā)環(huán)境,大部分會(huì)使用syso的方式或者日志方式打印性能損耗,如果代碼沒(méi)有加在運(yùn)行時(shí)才想起來(lái),或者想關(guān)注突然想起的函數(shù),換做以前,是需要重啟服務(wù)的,如果有VisualVM就可以直接查看耗時(shí)以及調(diào)用次數(shù)等情況。而不用打印、輸出日志來(lái)查看性能損耗。
  1. 如何處理
    對(duì)于性能損耗的函數(shù),根據(jù)業(yè)務(wù)邏輯可以進(jìn)行相應(yīng)的優(yōu)化,例如字符串處理、文件讀寫(xiě)方式、SQL語(yǔ)句優(yōu)化、多線程處理等等方式。

    由于性能優(yōu)化涉及的內(nèi)容很多,這里就不深入了。主要是告訴大家通過(guò)VisualVM來(lái)排查問(wèn)題的具體位置。
    

排查JAVA應(yīng)用程序線程鎖

  1. JAVA應(yīng)用程序線程鎖原因
    JAVA線程鎖的例子和原因網(wǎng)上一大堆,我也不在這里深入說(shuō)明,這里主要是否講如何使用VisualVM進(jìn)行排查。至于例子可以看這里:http://blog.csdn.net/fengzhe0411/article/details/6953370

這個(gè)例子比較極端,一般情況下,出現(xiàn)鎖競(jìng)爭(zhēng)激烈是比較常見(jiàn)的。

  1. 排查JAVA應(yīng)用程序線程鎖
    啟動(dòng) VisualVM,在應(yīng)用程序窗口,選擇對(duì)應(yīng)的JAVA應(yīng)用,在詳情窗口》線程標(biāo)簽(勾選線程可視化),查看線程生命周期狀態(tài),主要留意線程生命周期中紅色部分。

(1)綠色:代表運(yùn)行狀態(tài)。一般屬于正常情況。如果是多線程環(huán)境,生產(chǎn)者消費(fèi)者模式下,消費(fèi)者一直處于運(yùn)行狀態(tài),說(shuō)明消費(fèi)者處理性能低,跟不上生產(chǎn)者的節(jié)奏,需要優(yōu)化對(duì)應(yīng)的代碼,如果不處理,就可能導(dǎo)致消費(fèi)者隊(duì)列阻塞的現(xiàn)象。對(duì)應(yīng)線程的【RUNNABLE】狀態(tài)。

(2)藍(lán)色:代表線程休眠。線程中調(diào)用Thread.sleep()函數(shù)的線程狀態(tài)時(shí),就是藍(lán)色。對(duì)應(yīng)線程的【TIMED_WAITING】狀態(tài)。

(3)黃色:代表線程等待。調(diào)用線程的wait()函數(shù)就會(huì)出現(xiàn)黃色狀態(tài)。對(duì)應(yīng)線程的【W(wǎng)AITING】狀態(tài)。

(4)紅色:代碼線程鎖定。對(duì)應(yīng)線程的【BLOCKED】狀態(tài)。

  1. 分析解決JAVA應(yīng)用程序線程鎖
    發(fā)生線程鎖的原因有很多,我所遇到比較多的情況是多線程同時(shí)訪問(wèn)同一資源,且此資源使用synchronized關(guān)鍵字,導(dǎo)致一個(gè)線程要等另外一個(gè)線程使用完資源后才能運(yùn)行。例如再?zèng)]有連接池的情況下,同時(shí)訪問(wèn)數(shù)據(jù)庫(kù)接口。這種情況會(huì)導(dǎo)致性能的極具下降,解決的方案是增加連接池,或者修改訪問(wèn)方式。或者將資源粒度細(xì)化,類似ConCurrentHashMap中的處理方式,將資源分為多個(gè)更小粒度的資源,在更小粒度資源上來(lái)處理鎖,就可以解決資源競(jìng)爭(zhēng)激烈的問(wèn)題。]

參考文章

https://segmentfault.com/a/1190000009707894

https://www.cnblogs.com/hysum/p/7100874.html

http://c.biancheng.net/view/939.html

https://www.runoob.com/

https://blog.csdn.net/android_hl/article/details/53228348

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容