您好,登錄后才能下訂單哦!
微信公眾號(hào)【Java技術(shù)江湖】 一位阿里 Java 工程師的技術(shù)小站。作者黃小斜,專注 Java 相關(guān)技術(shù):SSM、SpringBoot、MySQL、分布式、中間件、集群、Linux、網(wǎng)絡(luò)、多線程,偶爾講點(diǎn)Docker、ELK,同時(shí)也分享技術(shù)干貨和學(xué)習(xí)經(jīng)驗(yàn),致力于Java全棧開發(fā)?。P(guān)注公眾號(hào)后回復(fù)”Java“即可領(lǐng)取 Java基礎(chǔ)、進(jìn)階、項(xiàng)目和架構(gòu)師等免費(fèi)學(xué)習(xí)資料,更有數(shù)據(jù)庫、分布式、微服務(wù)等熱門技術(shù)學(xué)習(xí)視頻,內(nèi)容豐富,兼顧原理和實(shí)踐,另外也將贈(zèng)送作者原創(chuàng)的Java學(xué)習(xí)指南、Java程序員面試指南等干貨資源)
cdn.com/9eedaaa588bef997bef63a7160fa349134bdb78c.jpeg">
VisualVM是一款免費(fèi)的JAVA虛擬機(jī)圖形化監(jiān)控分析工具。
1. 擁有 圖形化的監(jiān)控界面 。
VisualVM官方網(wǎng)站: http://visualvm.java.net/
VisualVM各版本下載頁面: http://visualvm.java.net/releases.html
下載 VisualVM時(shí)也應(yīng)該注意,不同的JDK版本對(duì)應(yīng)不同版本的VisualVM,具體根據(jù)安裝的JDK版本來下載第一的VisualVM。
下載版本參考: Java虛擬機(jī)性能管理神器 - VisualVM(4) - JDK版本與VisualVM版本對(duì)應(yīng)關(guān)系
備注:下列表中顯示1.3.6版本只適合JDK7和JDK8,可是我用1.3.6版還是可以監(jiān)控JDK1.6_45的版本。
Java虛擬機(jī)性能管理神器 - VisualVM (2) 監(jiān)控遠(yuǎn)程主機(jī)上的JAVA應(yīng)用程序
使用VisualVM監(jiān)控遠(yuǎn)程主機(jī)上JAVA應(yīng)用程序時(shí),需要開啟遠(yuǎn)程主機(jī)上的遠(yuǎn)程監(jiān)控訪問,或者在遠(yuǎn)程JAVA應(yīng)用程序啟動(dòng)時(shí),開啟遠(yuǎn)程監(jiān)控選項(xiàng),兩種方法,選擇其中一種就可以開啟遠(yuǎn)程監(jiān)控功能,配置完成后就可以在本地對(duì)遠(yuǎn)程主機(jī)上的JAVA應(yīng)用程序進(jìn)行監(jiān)控。
在 JAVA_HOME/bin目錄中,創(chuàng)建名稱為 jstatdAllPolicy文件(這個(gè)文件名稱也可以順便起,不過要與jstatd啟動(dòng)時(shí)指定名稱相同),將以下內(nèi)容拷貝到文件中。并保證文件的權(quán)限和用戶都正確。
grant codebase"file:${java.home}/../lib/tools.jar"{ permission java.security.AllPermission; };
在 JAVA_HOME/bin目錄中,執(zhí)行以下命令:
./jstatd -J-Djava.security.policy=jstatdAllPolicy
-p 1099
-J-Djava.rmi.server.hostname=192.168.xxx.xxx
jstatd命令描述以及參數(shù)說明:
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í)使用,最好加上。
在需要遠(yuǎn)程監(jiān)控的JVM啟動(dòng)時(shí),開啟遠(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
在本地VisualVM的應(yīng)用程序窗口,右鍵單擊【遠(yuǎn)程】》【添加遠(yuǎn)程主機(jī)】》【主機(jī)名】中輸入遠(yuǎn)程主機(jī)的IP地址,點(diǎn)擊【高級(jí)設(shè)置】輸入遠(yuǎn)程主機(jī)開啟的監(jiān)控端口,點(diǎn)擊【確定】完成配置。
如果一切正常,就可以看到遠(yuǎn)程主機(jī)上的JAVA應(yīng)用程序了。
Java虛擬機(jī)性能管理神器 - VisualVM (3) 排查JAVA應(yīng)用程序內(nèi)存泄漏
線上應(yīng)用部署完成后,運(yùn)行1~2天左右就會(huì)出現(xiàn)假死,或者某天早上8~10點(diǎn)高峰期間突然不處理數(shù)據(jù)了。由于在測(cè)試環(huán)境的壓力測(cè)試沒有做完全,也沒有遇到相關(guān)問題。情況出現(xiàn)后對(duì)客戶的使用造成很大影響,領(lǐng)導(dǎo)要求趕緊排查出問題原因!
排查原因前,與運(yùn)維溝通,了解線上服務(wù)器的運(yùn)行狀態(tài),通過ganglila觀察網(wǎng)絡(luò)、CPU、內(nèi)存、磁盤的運(yùn)行歷史狀態(tài),發(fā)現(xiàn)程序故障前,都有一波很高的負(fù)載,排查線上日志,負(fù)載來源在8~9點(diǎn)平臺(tái)接入數(shù)據(jù)量成倍增加,通過與產(chǎn)品和市場(chǎng)人員分析,此時(shí)段是用戶集中上班、接入平臺(tái)的高峰時(shí)段,訪問日志也顯示,業(yè)務(wù)場(chǎng)景正常,無網(wǎng)絡(luò)攻擊和安全問題。屬于產(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)程序異常前通過執(zhí)行指令,直接生成當(dāng)前JVM的dmp文件,15434是指JVM的進(jìn)程號(hào)
jmap -dump:format=b,file=serviceDump.dat
15434
由于第一種方式是一種事后方式,需要等待當(dāng)前JVM出現(xiàn)問題后才能生成dmp文件,實(shí)時(shí)性不高,第二種方式在執(zhí)行時(shí),JVM是暫停服務(wù)的,所以對(duì)線上的運(yùn)行會(huì)產(chǎn)生影響。所以建議第一種方式。
獲取到dmp文件后,就開始進(jìn)行分析。將服務(wù)器上的dmp文件拷貝到本地,然后啟動(dòng)本地的VisualVM,點(diǎn)擊菜單欄【文件】選項(xiàng),裝入dmp文件
打開dmp文件后,查看類標(biāo)簽,就能看到占用內(nèi)存的一個(gè)排行。
然后通過檢查中查找最大的對(duì)象,排查到具體線程和對(duì)象。
上列中的com.ctfo.trackservice.handler.TrackHandleThread#4就是重點(diǎn)排查對(duì)象。
通過代碼的比對(duì),在此線程中,有調(diào)用DAO接口,負(fù)責(zé)將數(shù)據(jù)存儲(chǔ)到數(shù)據(jù)庫中。而存儲(chǔ)到數(shù)據(jù)庫中時(shí),由于存儲(chǔ)速度較慢,導(dǎo)致此線程中的數(shù)據(jù)隊(duì)列滿了,數(shù)據(jù)積壓,無法回收導(dǎo)致了隊(duì)列鎖定,結(jié)果就是程序假死,不處理數(shù)據(jù)。
通過進(jìn)一步分析,發(fā)現(xiàn)數(shù)據(jù)庫存儲(chǔ)時(shí)有瓶頸,雖然當(dāng)前是批量提交,速度也不快。平均8000/秒的存儲(chǔ)速度。而數(shù)據(jù)庫有一個(gè)DG(備份)節(jié)點(diǎn),采用的是同步備份方式,即主庫事務(wù)要等DG的事務(wù)也完成后才能返回成功,這樣就會(huì)因?yàn)榫W(wǎng)絡(luò)因素、DG性能因素等原因?qū)е滦阅芟陆?。通過與DBA、產(chǎn)品、溝通,將同步備份改為異步備份,實(shí)時(shí)同步改為異步(異步可能會(huì)導(dǎo)致主備有10分鐘以內(nèi)的數(shù)據(jù)延遲)。速度達(dá)到30000/秒。問題解決。
至此,通過VisualVM分析java程序內(nèi)存泄漏到此結(jié)束。不過還有幾個(gè)問題:1. 如果dmp文件較大,VisualVM分析時(shí)間可能很久;另外,VisualVM對(duì)堆的分析顯示功能還不算全面。如果需要更全面的顯示,就可以使用另外一個(gè)專業(yè)的dmp文件分析工具【 Memory Analyzer (MAT) 】,此工具可以作為eclipse的插件進(jìn)行安裝,也可以單獨(dú)下載使用。如果有感興趣的朋友,我個(gè)人建議還是單獨(dú)下載使用。下載地址: http://www.eclipse.org/mat/
Java虛擬機(jī)性能管理神器 - VisualVM (4) 查找JAVA應(yīng)用程序耗時(shí)的方法函數(shù)
JAVA程序在開發(fā)前,根據(jù)設(shè)計(jì)文檔的性能需求,是要對(duì)程序的性能指標(biāo)進(jìn)行測(cè)試的。比如接口每秒響應(yīng)次數(shù)要求1000次/秒,就需要平均每次請(qǐng)求處理的時(shí)間在1ms以內(nèi),如果需要滿足這個(gè)指標(biāo),就需要在開發(fā)階段對(duì)接口執(zhí)行函數(shù)進(jìn)行監(jiān)控,也可以通過打印日志進(jìn)行監(jiān)控,從而統(tǒng)計(jì)對(duì)應(yīng)的性能指標(biāo),然后可以根據(jù)性能指標(biāo)的要求進(jìn)行相應(yīng)優(yōu)化。
根據(jù)具體業(yè)務(wù)的場(chǎng)景和需求,主要集中在IO通訊、 文件讀寫、 數(shù)據(jù)庫操作、業(yè)務(wù) 邏輯處理上,這些都是制約性能的重要因素,所以需要重點(diǎn)關(guān)注。
在研發(fā)環(huán)境,大部分會(huì)使用syso的方式或者日志方式打印性能損耗,如果代碼沒有加在運(yùn)行時(shí)才想起來,或者想關(guān)注突然想起的函數(shù),換做以前,是需要重啟服務(wù)的,如果有VisualVM就可以直接查看耗時(shí)以及調(diào)用次數(shù)等情況。而不用打印、輸出日志來查看性能損耗。
Java虛擬機(jī)性能管理神器 - VisualVM (5) 排查JAVA應(yīng)用程序線程鎖
JAVA線程鎖的例子和原因網(wǎng)上一大堆,我也不在這里深入說明,這里主要是否講如何使用VisualVM進(jìn)行排查。至于例子可以看這里: http://blog.csdn.net/fengzhe0411/article/details/6953370
這個(gè)例子比較極端,一般情況下,出現(xiàn)鎖競(jìng)爭(zhē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),說明消費(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)。
發(fā)生線程鎖的原因有很多,我所遇到比較多的情況是 多線程同時(shí)訪問同一資源,且此資源使用 synchronized關(guān)鍵字,導(dǎo)致一個(gè)線程要等另外一個(gè)線程使用完資源后才能運(yùn)行。例如再?zèng)]有連接池的情況下,同時(shí)訪問數(shù)據(jù)庫接口。這種情況會(huì)導(dǎo)致性能的極具下降,解決的方案是增加連接池,或者修改訪問方式?;蛘邔①Y源粒度細(xì)化,類似ConCurrentHashMap中的處理方式,將資源分為多個(gè)更小粒度的資源,在更小粒度資源上來處理鎖,就可以解決資源競(jìng)爭(zhēng)激烈的問題。]
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。