問題描述:
(2015-06)權(quán)限系統(tǒng)每運(yùn)行一段時(shí)間被济,cpu的使用率高居不下救赐,按道理,4個(gè)核的CPU只磷,即使線程經(jīng)常100%是沒問題的经磅,畢竟沒到400%地滿載,但是對(duì)于一個(gè)并發(fā)量不高的權(quán)限系統(tǒng)钮追,這往往有潛在問題预厌。
Top:
f 或者F 從當(dāng)前顯示中添加或者刪除項(xiàng)目。
o 或者O 改變顯示項(xiàng)目的順序元媚。
l 切換顯示平均負(fù)載和啟動(dòng)時(shí)間信息轧叽。
m 切換顯示內(nèi)存信息。
t 切換顯示進(jìn)程和CPU狀態(tài)信息惠毁。
c 切換顯示命令名稱和完整命令行犹芹。
M 根據(jù)駐留內(nèi)存大小進(jìn)行排序。
P 根據(jù)CPU使用百分比大小進(jìn)行排序鞠绰。
T 根據(jù)時(shí)間/累計(jì)時(shí)間進(jìn)行排序腰埂。
1、使用top命令蜈膨,然后按c鍵屿笼,根據(jù)command的詳細(xì)信息找出對(duì)應(yīng)的java進(jìn)程PID(ps、jps等方法也是OK的翁巍,方法可謂五花八門了)驴一,這里是2965;
2灶壶、top -p 2965肝断,然后再按H(H是顯示當(dāng)前進(jìn)程中的各線程),找到占cpu為100%左右的某線程(線程的PID為:2970)
3、將2970轉(zhuǎn)16進(jìn)制:B9A(快捷鍵win+r-->輸入calc-->使用程序員計(jì)算器)
4胸懈、使用jstack -l 2965 | grep B9A找到當(dāng)前線程担扑,結(jié)果是:"Concurrent Mark-Sweep GC Thread" prio=10 tid=0x0000000053293800 nid=0x*** runnable
5、而Concurrent Mark-Sweep GC Thread趣钱,是JVM的標(biāo)記清除線程涌献,可見GC太頻繁,由于JVM中根據(jù)對(duì)象的存活周期的不同將內(nèi)存劃分為幾塊首有。把java堆分為新生代和老年代燕垃,根據(jù)各個(gè)年代的特點(diǎn)采用適當(dāng)?shù)氖占惴āT谛律看卫占瘯r(shí)都發(fā)現(xiàn)有大批對(duì)象死去卜壕,只有少量幸存者,選用復(fù)制算法從from區(qū)到to區(qū)低矮,只需要付出少量存活對(duì)象的復(fù)制成本就可以完成收集印叁。而老年代中因?yàn)閷?duì)象存活率高、并沒有額外空間對(duì)他進(jìn)行分配擔(dān)保军掂,就使用“標(biāo)記-整理”算法進(jìn)行回收轮蜕,本次的內(nèi)存漏問題主要發(fā)生在老齡代。
6蝗锥、為了確定一下以上推測跃洛,再使用 jmap -heap 2965(注意:jmap不是運(yùn)行分析工具,在生成結(jié)果時(shí)JVM可能會(huì)掛起终议,因此當(dāng)生成統(tǒng)計(jì)結(jié)果時(shí)需要確認(rèn)這種暫停對(duì)程序是可接受的)汇竭,查看得老齡區(qū)占用內(nèi)存已達(dá)99.99%(而老齡區(qū)的內(nèi)存設(shè)置為: capacity = 2147483648 (2048.0MB));使用jmap -dump:format=b,file=dump.65 2965將內(nèi)存dump成文件穴张,將內(nèi)存文件拿下來分析细燎;
(可以不拿下來,直同樣直接可以在服務(wù)器上使用jhat對(duì)dump文件分析皂甘,jhat默認(rèn)會(huì)打開7000端口的web服務(wù)玻驻;并具如果dump文件過大,jhat需要使用的xmx參數(shù)也需要注意設(shè)置偿枕,否則OOM璧瞬;jhat -J-mx4G -port 8080 D:\klive\midea\wspace\document\data\dump.65,但注意端口是否開放渐夸,是否沖突嗤锉,機(jī)器是否夠內(nèi)存,詳細(xì)分析過程不寫了)
為了使結(jié)果更明顯借用第三方工具:
7墓塌、下載memory analyzer tool工具進(jìn)行dump文件分析瘟忱,由于本人使的是intelij idea奥额,而這個(gè)idea沒有相對(duì)應(yīng)的MAT插件,就懶得下載mat對(duì)應(yīng)的eclipse的插件而是下載具有能獨(dú)立運(yùn)行的mat版本酷誓,具體地址是http://www.eclipse.org/mat/downloads.php披坏,具體的eclipse插件可參考:http://blog.csdn.net/yanghongchang_/article/details/7711911
8态坦、由于dump下來的文件大約3.2G盐数,而想下載到工作的電腦上,這個(gè)是文件是大了點(diǎn)伞梯,使用tar -czf dum.tar.gz dump.65命令進(jìn)行壓縮玫氢,(加z是tar調(diào)用gzip進(jìn)行壓縮),具體的壓縮命令谜诫,請(qǐng)參考:http://www.jb51.net/LINUXjishu/43356.html結(jié)果為600多M漾峡,雖然還很大,但卻小了很多喻旷∩荩【bzip2是一個(gè)壓縮能力更強(qiáng)的壓縮程序,.bz2結(jié)尾的文件就是bzip2壓縮的結(jié)果且预。與bzip2相對(duì)的解壓程序是bunzip2槽袄。tar中使用-j這個(gè)參數(shù)來調(diào)用gzip。下面來舉例說明一下:# tar -cjf all.tar.bz2 *.jpg】
9锋谐、由于dump文件過大遍尺,所要以需要修改MAT啟動(dòng)的xmx參數(shù):到mat安裝的根目錄下找到:MemoryAnalyzer.ini,修改-Xmx的值4096m涮拗;
10乾戏、啟動(dòng)mat,加載dump文件進(jìn)行結(jié)果分析三热,不看不知道鼓择,一看挺恐怖javax.crypto.SunJCE_b這個(gè)類竟然占了2.1G內(nèi)存;
結(jié)果一目了然:
org.bouncycastle.jce.provider.BouncyCastleProvider()的實(shí)例就漾,一直回收不了呐能,快撐爆了2G的老齡區(qū);
后來分析知道的這個(gè)是用戶解密的代碼問題从藤。檢查common包的RSAUtil類催跪,結(jié)果也是一目了然,這代碼估計(jì)也是copy網(wǎng)上的然后沒仔細(xì)檢查一下夷野,二話沒說地使用了懊蒸。
[圖片上傳失敗...(image-41fd49-1597917382142)]
總結(jié):這問題告訴我們這種結(jié)果不是我們想要;
備注:更詳細(xì)的MAT分析:http://seanhe.iteye.com/blog/898277
/*******************************我是分割線************************************/
二悯搔、RSA加解密BC類導(dǎo)致的內(nèi)存溢出(2015-08-28)
1.事由:權(quán)限模塊長時(shí)間運(yùn)行會(huì)出現(xiàn)CPU100%的情況骑丸,用JVM內(nèi)存泄漏分析得出是登錄的時(shí)候會(huì)對(duì)用戶密碼進(jìn)行解密的方法導(dǎo)致的。
2.事發(fā)地點(diǎn):Cipher cipher = Cipher.getInstance("RSA",new org.bouncycastle.jce.provider.BouncyCastleProvider());
3.分析:
解密用到的org.bouncycastle.jce.provider.BouncyCastleProvider 補(bǔ)充:Bouncy Castle 是一種用于 Java 平臺(tái)的開放源碼的輕量級(jí)密碼術(shù)包,負(fù)責(zé)密碼術(shù)算法通危。
看一下BC的源碼铸豁,在這個(gè)問題中,每一次解密都new 一個(gè)bc菊碟,而new bc時(shí)會(huì)執(zhí)行setup的方法节芥,這個(gè)方法就是負(fù)責(zé)把密碼術(shù)的算法初始化并放到罪魁禍?zhǔn)譴inkMap里面。
雖然解密的時(shí)候都是new 一個(gè)bc, 但為什么沒有被GC呢逆害?下一步看 Cipher這個(gè)引擎類在getInstance(String,Provider)這個(gè)方法里做了什么头镊。
可以看到實(shí)例化的時(shí)候都會(huì)調(diào)用JceSecurity校驗(yàn)BC這個(gè)provider是否存在,由于我們是每一次解密都new一個(gè)bc魄幕,所以會(huì)一只往面放值相艇。verifyingProviders和verificationResults這兩個(gè)map里
看verifyingProviders和verificationResults的定義
由于是final+static不在gc之列,所以權(quán)限模塊一直沒有回收這兩個(gè)map,最終導(dǎo)致LinkMap的爆滿