您好,登錄后才能下訂單哦!
JDK自帶的JAVA性能分析工具。它已經(jīng)在你的JDK bin目錄里了,只要你使用的是JDK1.6 Update7之后的版本。點(diǎn)擊一下jvisualvm.exe圖標(biāo)它就可以運(yùn)行了。
這里是VisualVM 的官方網(wǎng)站:https://visualvm.dev.java.net,資料很全,同時(shí)提供VisualVM最近版本下載。
只要安裝JDK即可,運(yùn)行jvisualvm.exe ,選擇【工具】——【插件】——【可用插件】
·
要從遠(yuǎn)程應(yīng)用程序中檢索數(shù)據(jù),需要在遠(yuǎn)程 JVM 上運(yùn)行 jstatd 實(shí)用程序。即要進(jìn)行以下操作:
1)在jdk 安裝目錄的bin目錄下新建文件jstatd.all.policy,文件內(nèi)容為:
grant codebase "file:${java.home}/../lib/tools.jar" {
permission java.security.AllPermission;
};
2)再新建文件jstatd.sh ,文件內(nèi)容為:
./jstatd -J-Djava.security.policy=jstatd.all.policy
3)啟動(dòng)jstat : nohup jstatd.sh & (默認(rèn)啟動(dòng)端口為1099)
4)配置resin.conf,把以下注釋打開:
<!-- no use args
<jvm-arg>-Xdebug</jvm-arg>
<jvm-arg>-Dcom.sun.management.jmxremote</jvm-arg>
1)在應(yīng)用的resin配置文件中加配置:
<jvm-arg>-Dcom.sun.management.jmxremote</jvm-arg>
<jvm-arg>-Dcom.sun.management.jmxremote.port=9009</jvm-arg>
<jvm-arg>-Dcom.sun.management.jmxremote.ssl=false</jvm-arg>
<jvm-arg>-Dcom.sun.management.jmxremote.authenticate=false</jvm-arg>
2)選擇Tools菜單里的Plugins菜單,點(diǎn)擊 'VisualVM-JConsole' 然后點(diǎn)擊'Install' 按鈕
3)安裝完畢后重新啟動(dòng)VisualVm
4)配置JConsole頁簽,點(diǎn)擊'JConsole Plugins'按鈕
5)點(diǎn)擊'Add JAR/Folder'按鈕,
6)添加JDK_HOME/demo/management/JTop/JTop.jar
7)重新打開監(jiān)控頁面,可以看到JConsole正常工作
點(diǎn)采樣器欄:
點(diǎn)擊cpu,觀察到cpu使用狀況,點(diǎn)擊snapshot,采取結(jié)果,可以選擇查看方法、類或包的cpu使用情況,使用工具可以查找想要查看的方法、類或包:
1)選擇要監(jiān)控的pid,查看GC情況
如果Old區(qū)滿了,每次回收都很少或者回收不了,說明GC有問題。
1)內(nèi)存泄露、溢出的異同
同:都會導(dǎo)致應(yīng)用程序運(yùn)行出現(xiàn)問題,性能下降或掛起。
異:
v內(nèi)存泄露是導(dǎo)致內(nèi)存溢出的原因之一;內(nèi)存泄露積累起來將導(dǎo)致內(nèi)存溢出。
v內(nèi)存泄露可以通過完善代碼來避免;內(nèi)存溢出可以通過調(diào)整配置來減少發(fā)生頻率,但無法徹底避免。
2)監(jiān)測內(nèi)存泄漏
·內(nèi)存泄漏是指程序中間動(dòng)態(tài)分配了內(nèi)存,但在程序結(jié)束時(shí)沒有釋放這部分內(nèi)存,從而造成那部分內(nèi)存不可用的情況,重啟計(jì)算機(jī)可以解決,但也有可能再次發(fā)生內(nèi)存泄露,內(nèi)存泄露和硬件沒有關(guān)系,它是由軟件設(shè)計(jì)缺陷引起的。
·內(nèi)存泄漏可以分為4類:
a. 常發(fā)性內(nèi)存泄漏。發(fā)生內(nèi)存泄漏的代碼會被多次執(zhí)行到,每次被執(zhí)行的時(shí)候都會導(dǎo)致一塊內(nèi)存泄漏。
b.偶發(fā)性內(nèi)存泄漏。發(fā)生內(nèi)存泄漏的代碼只有在某些特定環(huán)境或操作過程下才會發(fā)生。常發(fā)性和偶發(fā)性是相對的。對于特定的環(huán)境,偶發(fā)性的也許就變成了常發(fā)性的。所以測試環(huán)境和測試方法對檢測內(nèi)存泄漏至關(guān)重要。
c.一次性內(nèi)存泄漏。發(fā)生內(nèi)存泄漏的代碼只會被執(zhí)行一次,或者由于算法上的缺陷,導(dǎo)致總會有一塊僅且一塊內(nèi)存發(fā)生泄漏。比如,在類的構(gòu)造函數(shù)中分配內(nèi)存,在析構(gòu)函數(shù)中卻沒有釋放該內(nèi)存,所以內(nèi)存泄漏只會發(fā)生一次。
d.隱式內(nèi)存泄漏。程序在運(yùn)行過程中不停的分配內(nèi)存,但是直到結(jié)束的時(shí)候才釋放內(nèi)存。嚴(yán)格的說這里并沒有發(fā)生內(nèi)存泄漏,因?yàn)樽罱K程序釋放了所有申請的內(nèi)存。但是對于一個(gè)服務(wù)器程序,需要運(yùn)行幾天,幾周甚至幾個(gè)月,不及時(shí)釋放內(nèi)存也可能導(dǎo)致最終耗盡系統(tǒng)的所有內(nèi)存。所以,我們稱這類內(nèi)存泄漏為隱式內(nèi)存泄漏。
3)Heap dump 分析
每隔一段時(shí)間給所檢測的java應(yīng)用做一次heap dump:
(或者在響應(yīng)應(yīng)用pid上鼠標(biāo)右鍵heap dump)彈出以下提示框:
在應(yīng)用服務(wù)器將此文件下載到jvisual vm所在的機(jī)器上,file--load打開此文件,如下面三圖所示:
對比上面三個(gè)截圖,發(fā)現(xiàn)似乎有個(gè)東西在急速飆升,仔細(xì)一看是這個(gè)對象:org.eclipse.swt.graphics.Image 在第一章圖中這個(gè)還沒排的上,第二次dump已經(jīng)上升到5181個(gè),第三次就是7845個(gè)了。漲速相當(dāng)快,而且和任務(wù)管理器里面看到的GDI數(shù)量增漲一致,就是它了。
問題到這兒就比較清楚了,回到代碼里面仔細(xì)一看可以發(fā)現(xiàn),是某個(gè)地方反復(fù)的用圖片來創(chuàng)建Image對象導(dǎo)致的,改掉以后搞定問題。
到這里其實(shí)我想說的是,Java使用起來其實(shí)要比C++更容易導(dǎo)致內(nèi)存泄漏。對于C++來說,每一個(gè)申請的對象都必須明確釋放,任何沒有釋放的對象都會導(dǎo)致memleak,這是不可饒恕的,而且這類問題已經(jīng)有很多工具和方法來解決。但是到了Java里面情況就不同了,對于Java程序員來說對象都是不需要也無法主動(dòng)銷毀的,所以一般的思路是:隨用隨new,用完即丟。很多對象具體的生命周期可能連寫代碼的人自己也不清楚,或者不需要清楚,只知道某個(gè)時(shí)刻垃圾收集器會去做的,不用管。但很可能某個(gè)對象在“用完即丟”的時(shí)候在另一個(gè)不容易發(fā)現(xiàn)的地方被保存了一個(gè)引用,那么這個(gè)對象就永遠(yuǎn)不會被回收。更加糟糕的是整個(gè)程序從設(shè)計(jì)之初就沒有考慮過對象回收的問題,對于C++程序員來說memleak必然是一個(gè)設(shè)計(jì)錯(cuò)誤,但是對Java程序員來說這只是一個(gè)疏忽,而且似乎沒有什么好的辦法來避免。今天看到的這個(gè)問題是因?yàn)镚DI泄漏會造成嚴(yán)重后果才被重視,但如果僅僅是造成內(nèi)存泄漏,那這個(gè)程序可能得連續(xù)跑上個(gè)十天半個(gè)月才會發(fā)現(xiàn)問題。最后就是,對于c++,錯(cuò)誤的代碼在測試階段就可以快速的偵測出哪怕一個(gè)byte的memleak并加以改正,但是對于java程序,理論上沒有哪個(gè)工具能夠知道是不是有泄漏,因?yàn)槌俗髡咦约阂酝鉀]有人能夠知道一個(gè)被引用的對象是不是應(yīng)該被銷毀,只有通過大量的,長期的壓力測試才能發(fā)現(xiàn)問題,這是很危險(xiǎn)的一件事情。
4)解決內(nèi)存溢出問題
1、java.lang.OutOfMemoryError: PermGen space
JVM管理兩種類型的內(nèi)存,堆和非堆。堆是在JVM啟動(dòng)時(shí)創(chuàng)建;非堆是留給JVM自己用的,用來存放類的信息的。它和堆不同,運(yùn)行期內(nèi)GC不會釋放空間。如果web app用了大量的第三方j(luò)ar或者應(yīng)用有太多的class文件而恰好MaxPermSize設(shè)置較小,超出了也會導(dǎo)致這塊內(nèi)存的占用過多造成溢出,或者tomcat熱部署時(shí)侯不會清理前面加載的環(huán)境,只會將context更改為新部署的,非堆存的內(nèi)容就會越來越多。
PermGen space的全稱是Permanent Generation space,是指內(nèi)存的永久保存區(qū)域,這塊內(nèi)存主要是被JVM存放Class和Meta信息的,Class在被Loader時(shí)就會被放到PermGen space中,它和存放類實(shí)例(Instance)的Heap區(qū)域不同,GC(Garbage Collection)不會在主程序運(yùn)行期對PermGen space進(jìn)行清理,所以如果你的應(yīng)用中有很CLASS的話,就很可能出現(xiàn)PermGen space錯(cuò)誤,這種錯(cuò)誤常見在web服務(wù)器對JSP進(jìn)行pre compile的時(shí)候。如果你的WEB APP下都用了大量的第三方j(luò)ar, 其大小超過了jvm默認(rèn)的大小(4M)那么就會產(chǎn)生此錯(cuò)誤信息了。
如上圖所示,PermGen在程序運(yùn)行一段時(shí)間后超出了我們指定的128MB,通過Classes視圖看到,Java在運(yùn)行的同時(shí)加載了大量的類到內(nèi)存中。PermGen會存儲Jar或者Class的描述信息;所以在class大量增加的同時(shí)PermGen超出了我們指定的范圍。為了讓應(yīng)用穩(wěn)定,我們需要探尋新的PermGen范圍。檢測時(shí)段時(shí)候后(如下圖)發(fā)現(xiàn)PermGen在145MB左右穩(wěn)定,那么我們就得到了穩(wěn)定的新參數(shù);這樣PermGen內(nèi)存溢出的問題得到解決。
2、java.lang.OutOfMemoryError: Java heap space
第一種情況是個(gè)補(bǔ)充,主要存在問題就是出現(xiàn)在這個(gè)情況中。其默認(rèn)空間(即-Xms)是物理內(nèi)存的1/64,最大空間(-Xmx)是物理內(nèi)存的1/4。如果內(nèi)存剩余不到40%,JVM就會增大堆到Xmx設(shè)置的值,內(nèi)存剩余超過70%,JVM就會減小堆到Xms設(shè)置的值。所以服務(wù)器的Xmx和Xms設(shè)置一般應(yīng)該設(shè)置相同避免每次GC后都要調(diào)整虛擬機(jī)堆的大小。假設(shè)物理內(nèi)存無限大,那么JVM內(nèi)存的最大值跟操作系統(tǒng)有關(guān),一般32位機(jī)是1.5g到3g之間,而64位的就不會有限制了。
注意:如果Xms超過了Xmx值,或者堆最大值和非堆最大值的總和超過了物理內(nèi)存或者操作系統(tǒng)的最大限制都會引起服務(wù)器啟動(dòng)不起來。
垃圾回收GC的角色,JVM調(diào)用GC的頻度還是很高的,主要兩種情況下進(jìn)行垃圾回收:
一個(gè)是當(dāng)應(yīng)用程序線程空閑;另一個(gè)是java內(nèi)存堆不足時(shí),會不斷調(diào)用GC,若連續(xù)回收都解決不了內(nèi)存堆不足的問題時(shí),就會報(bào)out of memory錯(cuò)誤。因?yàn)檫@個(gè)異常根據(jù)系統(tǒng)運(yùn)行環(huán)境決定,所以無法預(yù)期它何時(shí)出現(xiàn)。
根據(jù)GC的機(jī)制,程序的運(yùn)行會引起系統(tǒng)運(yùn)行環(huán)境的變化,增加GC的觸發(fā)機(jī)會。
為了避免這些問題,程序的設(shè)計(jì)和編寫就應(yīng)避免垃圾對象的內(nèi)存占用和GC的開銷。顯示調(diào)用System.GC()只能建議JVM需要在內(nèi)存中對垃圾對象進(jìn)行回收,但不是必須馬上回收。一個(gè)是并不能解決內(nèi)存資源耗空的局面,另外也會增加GC的消耗。
1)盡早釋放無用對象的引用。
好的辦法是使用臨時(shí)變量的時(shí)候,讓引用變量在退出活動(dòng)域后自動(dòng)設(shè)置為null,暗示垃圾收集器來收集該對象,防止發(fā)生內(nèi)存泄露。
2) 程序進(jìn)行字符串處理時(shí),盡量避免使用String,而應(yīng)使用StringBuffer。
因?yàn)槊恳粋€(gè)String對象都會獨(dú)立占用內(nèi)存一塊區(qū)域,如:
1.String str = "aaa";
2.String str2 = "bbb";
3.String str3 = str + str2;
4.// 假如執(zhí)行此次之后str , str2再不被調(diào)用,那么它們就會在內(nèi)存中等待GC回收;
5.// 假如程序中存在過多的類似情況就會出現(xiàn)內(nèi)存錯(cuò)誤;
3) 盡量少用靜態(tài)變量。
因?yàn)殪o態(tài)變量是全局的,GC不會回收。
4) 避免集中創(chuàng)建對象尤其是大對象,如果可以的話盡量使用流操作。
JVM會突然需要大量內(nèi)存,這時(shí)會觸發(fā)GC優(yōu)化系統(tǒng)內(nèi)存環(huán)境; 一個(gè)案例如下:
1.// 使用jspsmartUpload作文件上傳,運(yùn)行過程中經(jīng)常出現(xiàn)java.outofMemoryError的錯(cuò)誤,
2.// 檢查之后發(fā)現(xiàn)問題:組件里的代碼
3.m_totalBytes = m_request.getContentLength();
4.m_binArray = new byte[m_totalBytes];
5.// totalBytes這個(gè)變量得到的數(shù)極大,導(dǎo)致該數(shù)組分配了很多內(nèi)存空間,而且該數(shù)組不能及時(shí)釋放。
6.// 解決辦法只能換一種更合適的辦法,至少是不會引發(fā)outofMemoryError的方式解決。
7.// 參考:http://bbs.xml.org.cn/blog/more.asp?name=hongrui&id=3747
5) 盡量運(yùn)用對象池技術(shù)以提高系統(tǒng)性能。
生命周期長的對象擁有生命周期短的對象時(shí)容易引發(fā)內(nèi)存泄漏,例如大集合對象擁有大數(shù)據(jù)量的業(yè)務(wù)對象的時(shí)候,可以考慮分塊進(jìn)行處理,然后解決一塊釋放一塊的策略。
6) 不要在經(jīng)常調(diào)用的方法中創(chuàng)建對象,尤其是忌諱在循環(huán)中創(chuàng)建對象。
可以適當(dāng)?shù)氖褂胔ashtable,vector 創(chuàng)建一組對象容器,然后從容器中去取那些對象,而不用每次new之后又丟棄。
7) 優(yōu)化配置。
a.設(shè)置-Xms、-Xmx相等;
b.設(shè)置NewSize、MaxNewSize相等;
c.設(shè)置Heap size, PermGen space:
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。