加密工具類(lèi)導(dǎo)致內(nèi)存溢出分析總結(jié)

這是第一次把問(wèn)題分析的總結(jié)記錄下來(lái)终议,一是記錄下做備忘,二是把問(wèn)題分析的過(guò)程和總結(jié)梳理下葱蝗。

一共在兩個(gè)系統(tǒng)碰過(guò)因?yàn)榧用軐?dǎo)致OOM的問(wèn)題:
第一次遇到這個(gè)問(wèn)題的時(shí)候什么也不懂穴张,只知道渾身發(fā)抖心亂跳……。不知道問(wèn)題產(chǎn)生的原因更不知道該從何查起两曼,運(yùn)維同事給打了份dump日志皂甘,對(duì)我來(lái)說(shuō)什么用都沒(méi)有。沒(méi)辦法只能請(qǐng)當(dāng)時(shí)組里的牛人幫看悼凑。然后他就告訴我把一個(gè)變量設(shè)置成靜態(tài)的偿枕,修改后,發(fā)布到服務(wù)器上果然沒(méi)有再內(nèi)存飆升直至OOM了户辫。當(dāng)時(shí)也沒(méi)有請(qǐng)教下問(wèn)題的根本原因是什么渐夸,只是問(wèn)題解決就松了一口氣。
第二次是另外一個(gè)系統(tǒng)渔欢,但是那個(gè)系統(tǒng)不像第一次碰到的系統(tǒng)那樣發(fā)布上去碰到訪問(wèn)高峰就OOM墓塌。這個(gè)系統(tǒng)問(wèn)題發(fā)現(xiàn)的比較有意思,為什么說(shuō)有意思呢奥额?因?yàn)閱?wèn)題一直都存在苫幢,只不過(guò)加密工具類(lèi)調(diào)用的次數(shù)少,再加上這個(gè)系統(tǒng)發(fā)布比較頻繁垫挨,所以一直沒(méi)有OOM韩肝。直到有一次半個(gè)月沒(méi)有更新發(fā)布才報(bào)了OOM。

后來(lái)開(kāi)始學(xué)習(xí)了解jvm棒拂,嘗試著去模擬重現(xiàn)當(dāng)時(shí)的場(chǎng)景伞梯,然后分析系統(tǒng)OOM的原因玫氢。兩次問(wèn)題的共同點(diǎn)都是多次調(diào)用加密類(lèi)導(dǎo)致的。所以問(wèn)題應(yīng)該就在這個(gè)加密類(lèi)谜诫。
模擬的代碼如下:
public static voidencrypt(){ try{ Cipher cipher = Cipher.getInstance("RSA", newBouncyCastleProvider()); // cipher.init(); }catch(NoSuchAlgorithmException e) { e.printStackTrace(); }catch(NoSuchPaddingException e) { e.printStackTrace(); } }
jvm參數(shù)設(shè)置:
-Xmx10m -Xms10m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/baitianxia/Documents/oom/heapdump.hprof
循環(huán)調(diào)用上面的方法就可以制造OOM了漾峡。
既然問(wèn)題可以重現(xiàn),下面就可以開(kāi)始分析問(wèn)題的原因了:
jvm參數(shù)設(shè)置的是當(dāng)OOM的時(shí)候打印heap dump到指定目錄喻旷。分析heap dump文件常用的工具是MAT(Memory Analyzer Tool)生逸。
1)用MAT打開(kāi)heapdump.hprof文件的截圖如圖1,看到占用內(nèi)存最大的是餅圖中的深藍(lán)色部分且预,點(diǎn)擊顯示JceSecurity類(lèi)槽袄,那么可以初步斷定問(wèn)題是由這個(gè)類(lèi)導(dǎo)致的;

圖1

2)點(diǎn)擊Actions下的Dominator Tree可以查看占用內(nèi)存最大的對(duì)象锋谐,點(diǎn)擊后如圖2遍尺。可以看到有一個(gè)IdentityHashMap存儲(chǔ)了大量的BouncyCastleProvider對(duì)象涮拗;


圖2

3)點(diǎn)擊JceSecurity-->List Objects-->with outgoing references顯示如圖3所示乾戏,可以看到是變量名為verificationResults的identityHashMap中存放了大量的BouncyCastleProvier,基本上就已經(jīng)找到導(dǎo)致問(wèn)題的原因了三热;

圖3

4)查看源碼鼓择,可以看到因?yàn)関erificationResults是靜態(tài)的,不會(huì)被GC就漾,所以隨著加密工具類(lèi)調(diào)用的次數(shù)增加呐能,verificationResults存儲(chǔ)的BouncyCastle也越來(lái)越多,最終導(dǎo)致OOM抑堡。

public static final Cipher getInstance(String var0, Provider var1) throws NoSuchAlgorithmException, NoSuchPaddingException {
        if(var1 == null) {
            throw new IllegalArgumentException("Missing provider");
        } else {
            Exception var2 = null;
            List var3 = getTransforms(var0);
            boolean var4 = false;
            String var5 = null;
            Iterator var6 = var3.iterator();

            while(true) {
                while(true) {
                    Cipher.Transform var7;
                    Service var8;
                    do {
                        do {
                            if(!var6.hasNext()) {
                                if(var2 instanceof NoSuchPaddingException) {
                                    throw (NoSuchPaddingException)var2;
                                }

                                if(var5 != null) {
                                    throw new NoSuchPaddingException("Padding not supported: " + var5);
                                }

                                throw new NoSuchAlgorithmException("No such algorithm: " + var0, var2);
                            }

                            var7 = (Cipher.Transform)var6.next();
                            var8 = var1.getService("Cipher", var7.transform);
                        } while(var8 == null);

                        if(!var4) {
                            Exception var9 = JceSecurity.getVerificationResult(var1);
                            if(var9 != null) {
                                String var12 = "JCE cannot authenticate the provider " + var1.getName();
                                throw new SecurityException(var12, var9);
                            }

                            var4 = true;
                        }
                    } while(var7.supportsMode(var8) == 0);

                    if(var7.supportsPadding(var8) != 0) {
                        try {
                            CipherSpi var13 = (CipherSpi)var8.newInstance((Object)null);
                            var7.setModePadding(var13);
                            Cipher var10 = new Cipher(var13, var0);
                            var10.provider = var8.getProvider();
                            var10.initCryptoPermission();
                            return var10;
                        } catch (Exception var11) {
                            var2 = var11;
                        }
                    } else {
                        var5 = var7.pad;
                    }
                }
            }
        }
    }
private static finalMap verificationResults =newIdentityHashMap();
static synchronized Exception getVerificationResult(Provider var0) {
        Object var1 = verificationResults.get(var0);
        if(var1 == PROVIDER_VERIFIED) {
            return null;
        } else if(var1 != null) {
            return (Exception)var1;
        } else if(verifyingProviders.get(var0) != null) {
            return new NoSuchProviderException("Recursion during verification");
        } else {
            Exception var3;
            try {
                verifyingProviders.put(var0, Boolean.FALSE);
                URL var2 = getCodeBase(var0.getClass());
                verifyProviderJar(var2);
                verificationResults.put(var0, PROVIDER_VERIFIED);
                var3 = null;
                return var3;
            } catch (Exception var7) {
                verificationResults.put(var0, var7);
                var3 = var7;
            } finally {
                verifyingProviders.remove(var0);
            }

            return var3;
        }
    }

至此摆出,問(wèn)題的根本原因已經(jīng)找到了。
解決方法就是將BouncyCastlePrivate 設(shè)置一個(gè)靜態(tài)的夷野,而不是每次都new一個(gè)懊蒸。

private static  BouncyCastleProvider bouncyCastleProvider = new BouncyCastleProvider();
Cipher cipher = Cipher.getInstance("RSA",bouncyCastleProvider);

另外說(shuō)一點(diǎn),之所以每次向IdentityHashMap類(lèi)型verificationResults中put new BouncyCastleProvider會(huì)導(dǎo)致OOM是因?yàn)镮dentityHashMap比較key值是否相等對(duì)比的是引用即“==”而HashMap是p.hash== hash &&
((k = p.key) == key || (key !=null&& key.equals(k)))悯搔,至于為什么verificationResults是IdentityHashMap類(lèi)型的還要再看看源碼才能知道骑丸。
現(xiàn)在回想下第一個(gè)系統(tǒng)OOM很好理解,第二個(gè)系統(tǒng)之所以一段時(shí)間不重啟才會(huì)OOM就是因?yàn)関erificationResults本身是靜態(tài)的再加上應(yīng)用調(diào)用加密工具類(lèi)的次數(shù)不多妒貌,所以才會(huì)有這種現(xiàn)象通危,比較有趣!我之所以強(qiáng)調(diào)verificationResults是靜態(tài)的灌曙,因?yàn)橹挥惺庆o態(tài)對(duì)象才會(huì)出現(xiàn)這種系統(tǒng)運(yùn)行一段時(shí)間才會(huì)OOM的現(xiàn)象菊碟。后面我會(huì)再寫(xiě)一個(gè)內(nèi)存溢出的案例。
參考書(shū):
深入理解Java虛擬機(jī):JVM高級(jí)特性與最佳實(shí)踐
內(nèi)存dump分析工具:
Memory Analyzer (MAT)
參考文檔:
內(nèi)存快照排查OOM在刺,加密時(shí)錯(cuò)誤方法指定provider方式錯(cuò)誤引起的OOM【原創(chuàng)】
這篇文章寫(xiě)得詳細(xì)逆害,非常推薦头镊,可以說(shuō)我寫(xiě)的基本是照抄他的,只是為了加深下自己的印象魄幕。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末相艇,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子纯陨,更是在濱河造成了極大的恐慌坛芽,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件翼抠,死亡現(xiàn)場(chǎng)離奇詭異咙轩,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)阴颖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)活喊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人膘盖,你說(shuō)我怎么就攤上這事胧弛∮任螅” “怎么了侠畔?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)损晤。 經(jīng)常有香客問(wèn)我软棺,道長(zhǎng),這世上最難降的妖魔是什么尤勋? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任喘落,我火速辦了婚禮,結(jié)果婚禮上最冰,老公的妹妹穿的比我還像新娘瘦棋。我一直安慰自己,他們只是感情好暖哨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布赌朋。 她就那樣靜靜地躺著,像睡著了一般篇裁。 火紅的嫁衣襯著肌膚如雪沛慢。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天达布,我揣著相機(jī)與錄音团甲,去河邊找鬼。 笑死黍聂,一個(gè)胖子當(dāng)著我的面吹牛躺苦,可吹牛的內(nèi)容都是我干的身腻。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼匹厘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼霸株!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起集乔,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤去件,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后扰路,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體尤溜,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年汗唱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宫莱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡哩罪,死狀恐怖授霸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情际插,我是刑警寧澤碘耳,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站框弛,受9級(jí)特大地震影響辛辨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望力喷。 院中可真熱鬧,春花似錦僻焚、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至猿挚,卻和暖如春咐旧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背绩蜻。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工铣墨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人办绝。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓伊约,卻偏偏與公主長(zhǎng)得像姚淆,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子屡律,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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