Java EE應用中的性能問題解決方案 — 第一部分 內存溢出的解決辦法及JVM內幕(D)

 聲明:本文禁止未經本人同意的任何形式轉載!如有轉載需求,可與本人通過個人資料中的電子郵箱聯繫。對於未經同意的轉載,本人將保留進一步行動的權利

可能較多數量的對內存泄露的錯誤判斷來自會話部分。會話部分並不會泄露任何內存。它消耗內存,表面上很像內存泄露,但最終會話內存還是會被回收的。如果應用服務器內存溢出,要想知道這個溢出情況是由於內存泄露引起的還是由於對會話管理不善造成的,最好的辦法是停止對應用服務器的請求,直至會話都失效後來觀察內存是否已經被回收。顯然,在生產環境下不太可能做這樣的監控,但使用這種辦法來判斷到底是因爲會話內容太多還是因爲內存泄露造成內存溢出是肯定不會有問題的。
 
總的來講,如果應用中過分使用了會話,則正確的解決之道就是調整應用的會話應用來降低內存的開銷。以下辦法可以將超大會話的影響降到最低:
  • 加大支持會話的堆尺寸
  • 減少會話失效時間
 尺寸越大的堆就會花更長的垃圾收集時間,所以並不是最佳的解決方案,但是總比內存溢出強些。增加堆的尺寸以支持會話在其生命週期內駐留可能需要足夠多的內存,計算方法是:活動用戶的會話內存量+會話失效週期內的會話內存量。如果業務邏輯允許,降低會話失效時間可能是更好的選擇。
 
總之,以下是按照優先級排列的解決步驟:
  • 調整應用減少會話內存用量
  • 鼓勵最終用戶及時登出系統
  • 強制降低會話失效時間
  • 增大堆尺寸
 
但是,在應用範圍內的變量(application)、靜態變量和長期生存的類都是真正需要在內存分析器中分析內存泄露的點。
 
永生代異常
JVM中永生代在處理內存中的用途往往被誤解了。堆本身只攜帶類實例,但在JVM能真正創建堆上的類實例時,必須先要將字節碼(.class)加載到處理內存中來。這樣才能使用這些字節碼在堆中創建類對象。JVM把類的不同版本的字節碼存放在永生代中。下圖即是永生代和堆的關係圖。永生代在JVM處理內存的內部,並不屬於堆。
 
 
通常,可能希望永生代能足夠大,大得能容納應用中所有的類,因爲這些類顯然會被實例化,而且從磁盤系統裏面讀取的代價要比從內存中讀取的代價昂貴。JVM使用-noclassgc調優選項來確保類不會從永生代中卸載。該選項不讓垃圾收集在永生代中運行。這個選項固然好,但有一個問題就是如果永生代滿了會如何?根據我的觀察,JVM會檢查永生代來確定其是否需要內存,因此就會觸發一個大規模的垃圾回收。但垃圾回收清理堆中的內存,如果加上了-nocloassgc,就不會動永生代的空間,所以這也是徒勞無果的。完成垃圾回收後,JVM又去查看永生代空間,發現還是沒有空間,於是就一遍一遍又一遍地執行垃圾回收!
 
我第一次遇到這個問題時,就是系統的性能極端低下,經過一段時間後就會內存溢出。經過對詳細垃圾回收日誌、堆空間利用和處理內存利用圖,很快就發現堆中的情況正常,但是處理內存溢出了。該系統中有數千個類和JSP頁面,再加上-noclassgc選項,永生代就產生了異常的情況。做調優處理時可以將永生代空間加大,並去掉-noclassgc的JVM選項。
 
 

如上圖所示,當永生代空間充滿後,就會觸發大規模的垃圾回收,將Eden和生存空間的對象清理到老生代中去。但如果加上-noclassgc選項,則不會對永生代的空間進行回收。

未完待續……
聲明:本文禁止未經本人同意的任何形式轉載!如有轉載需求,可與本人通過個人資料中的電子郵箱聯繫。對於未經同意的轉載,本人將保留進一步行動的權利!
發佈了13 篇原創文章 · 獲贊 0 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章