java case 3:方法區(PermGen)記憶體快速飆升問題,casepermgen

來源:互聯網
上載者:User

java case 3:方法區(PermGen)記憶體快速飆升問題,casepermgen

歡迎訪問:http://www.cloudnoter.com/?p=143

自從平台升級到3.0後,應用的JVM變得非常不穩定,主要體現為以下三個問題:

1.記憶體流失:2G的JVM,2天就崩。
2.方法區記憶體持續飆升,最終導致頻繁的觸發FullGC
3.class load頻繁導致CPU有30%的資源浪費


在寫之前先吐槽下:這個自研的JPA組件真TM坑人,放著開源的不用,非得自己實現,三個問題都是自研的JPA組件引入。


解決方案:
問題1:
問題1相對好解決,先用jmap將堆快照dump出來,用mat分析了下,根據GC-ROOT找到引用路徑即可,泄漏原因為:平台自研JPA組件的SQLQuery在實現lazy load時,由於CGLib使用不當(在向當前線程註冊回調方法攔截器時,在使用完之後未及時登出)導致的查詢結果緩衝被線程池中的線程引用,線上程池容量開得比較大時最終將導致OOM異常。


問題2,3:
    以前從沒碰到這種情況,方法區的記憶體大小在應用啟動後應該是處於一個相對穩定的狀態(因為大部分類在啟動時就已經載入完了,就算使用CGLib動態組建代理程式類也應該是有一個上限,最多就是全部類的一倍),但問題2明顯不屬於這種情況,不管開多大的記憶體給方法區(通過-X:MaxPermSize=xxxM設定大小),應用總能在幾分鐘內持續升到最高值並觸發FullGC,GC結束後,方法區佔用記憶體降至接近0M(此處就發生的class unload),然後又進入新一輪的飆升周期(此處就發生class loader)。
    剛開始以為仍然是JPA組件使用CGLib不當的問題,認為是為了實現lazy load及許可權控制時使用了過多的動態代理(每個Action,Model,Service都被建立為動態代理,更不合理的時每個model的get方法都使用ProxyMethodInterceptor,問當事人原因,其回覆說為了lazy load,但其實只有關聯欄位,集合欄位才有必要lazy load)。基於此做了修改,但測試結果還是沒解決問題:因為JPA中並不是每次都建立一個新的proxy,而是根據class做了緩衝的,因此只能另找辦法。
    既然是方法區的問題,那是否可以將方法區的內容dump出來呢?於是查看了下jmap參數,其中的有 -permstat可以用:jmap -permstat <pid> 
結果如下(截取)
25007 intern Strings occupying 2799672 bytes.class_loader classesbytes parent_loaderalive? type<bootstrap> 262415108248  null  live <internal>0x000000076f045910 38758792 0x00000007617d4f70dead com/atomikos/util/ClassLoadingHelper$1@0x0000000741cb86e80x000000076f942f10 38758792 0x00000007617d4f70dead com/atomikos/util/ClassLoadingHelper$1@0x0000000741cb86e80x00000007d00e9ba0 40816816 0x00000007617d4f70dead com/atomikos/util/ClassLoadingHelper$1@0x0000000741cb86e80x00000007dd170c08 40816816 0x00000007617d4f70dead com/atomikos/util/ClassLoadingHelper$1@0x0000000741cb86e80x000000079e2f0070 40816816 0x00000007617d4f70dead com/atomikos/util/ClassLoadingHelper$1@0x0000000741cb86e80x00000007cd6b1140 13112  null  dead sun/reflect/DelegatingClassLoader@0x00000007400676480x000000076fb5d130 38758792 0x00000007617d4f70dead com/atomikos/util/ClassLoadingHelper$1@0x0000000741cb86e80x000000076fbe47c0 38758792 0x00000007617d4f70dead com/atomikos/util/ClassLoadingHelper$1@0x0000000741cb86e8


com/atomikos/util/ClassLoadingHelper$1:是一個匿名內部類(該類是一個載入器),通過這個內部類載入器作為JDK Proxy.newProxyInstance()方法的參數,而後者就會生產大量的以Proxy$為首碼的動態類,並且未做任何緩衝。
atomikos大家應該都很清楚:JTA的一個實現,但是哪個組件調用了這個工具類呢?通過斷點分析,原來是JPA又自己寫了個什麼資料庫連接池,池中的每個串連都是ProxyConnection,而池又好像失效的,頻繁的回收,建立......(到目前為止,我一直沒搞清楚為何要用代理類型的串連,這代理的作用從代碼中也沒看出個門道來,也不是為做監控。)
原因定位到了,解決辦法就很簡單了:直接用阿里的druid替換掉。測試結果證明前面的分析是正確的。




結論:程式員是要建立價值的,而不是重複造輪子,要擁抱開源,不能閉門造車。



聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.