"JAVA內存泄漏"一個永恆的主題

"JAVA內存泄漏"一個永恆的主題
 
陳長君 2006-02-06 09:19  
  
內存不足與泄漏(OutOfMemoryError,以下簡稱OOM)的發生,就像系統的性能一樣,對於JAVA開發人員來說是一個十分棘手的問題,也是讓許多開發人員陷於束手無策的局面,而這兩個問題卻又時常困擾着每個JAVA軟件系統。

本文主要來探討關於JAVA內存泄漏的問題,首先簡述JVM的基礎概念,而後是關於西湖小學網站信息發佈系統內存泄漏問題解決全過程的講述。


JVM的內存管理


一、 相關概念

1. JAVA堆

JAVA堆指JVM用來存儲分配 JAVA 對象的這部分內存。通過-Xmx參數設置JAVA 堆內存的最大值,-Xms參數設置堆內存的最小值。建議您指定最大的 JAVA 堆值,因爲若未指定最大的堆大小,那麼該極限值由 JVM 根據諸如計算機中的物理內存量和該時刻的可用空閒內存量這類因素來決定。


2. 本地內存(Permanent Generation)

本地內存指JVM用於其自身內部操作的內存,通常也稱永久域。JVM 將使用的本地內存的大小取決於生成的代碼量、創建的線程、GC 期間用於保存 java 對象信息的內存,以及在代碼生成、優化等過程中使用的臨時空間。如果有一個第三方本地模塊,那麼它也可能使用本地內存。例如,本地 JDBC 驅動程序將分配本地內存。

系統默認的永久域大小是4M,但可以通過設置參數-XX:PermSize以及-XX:MaxPermSize進行本地內存大小的修改。但最大的永久域大小受到任何特定操作系統上虛擬進程大小的限制,也受到用.Xmx標誌指定用於 java 堆的內存量的限制。

3. 進程與線程

進程大小將是JAVA堆、本地內存與加載的可執行文件和庫所佔用內存的總和。在 32 位操作系統上,進程的虛擬地址空間最大可達到 4 GB。從這 4 GB 內存中,操作系統內核爲自己保留一部分內存(通常爲 1 - 2 GB),剩餘內存可用於應用程序。

線程是一個進程中的一個子序列,由程序負責管理。對於每個操作系統而言,線程也是一種資源,對於允許線程數的多少也都有各自的定義。

4. JAVA堆的細分

JAVA堆還可細分爲新域和舊域(New/Old Generation),JVM將生成的所有新對象放在新域中,在當對象經歷了一定數量的垃圾收集循環後,便獲得使用期並進入舊域。其中新域又可分成三個部分:第一部分爲Eden Space,用於存放生成新的對象,另兩部分都稱爲救助空間(Survivor Space)。當然具體對象的存放與特定的拉圾回收算法有着密切的相關。


二、 拉圾回收算法

當程序滿足拉圾回收條件時,JVM開始以一定的回收算法進行無用對象內存的釋放。一般JVM會採用多種的回收算法,同時對於不同的JVM所採用的回收算法也不同。拉圾回收算法主要有以下幾種:(更多具體內容,請讀者搜索相關資料)
    標記-清除收集器
    標記-壓縮收集器
    複製收集器
    增量收集器
    分代收集器
    併發收集器
    並行收集器


三、 爲什麼發生OOM問題

爲什麼會發生OOM?主要大方向可歸結於以下兩方面,在文章的後面會有具體的一些實例分析。

1. JAVA堆中的內存不足

如果JVM不能在JAVA堆中獲得更多內存來分配更多JAVA 對象,將會拋出 JAVA 內存不足 (JAVA OOM) 錯誤。如果JAVA堆充滿了活動對象,並且 JVM 無法再擴展 java 堆,那麼它將不能分配更多 JAVA 對象。

在這種情況下,JVM 讓應用程序決定在拋出 java.lang.OutOfMemoryError 後該執行什麼操作。例如,應用程序可以處理此錯誤,並決定以安全方式自行關閉或決定忽略此錯誤。如果應用程序不處理此錯誤,那麼拋出此錯誤的線程將退出(如果您進行 java Thread Dump,那麼將看不到該線程)。

在使用 Weblogic Server 的情況下,如果此錯誤是由某個執行線程拋出的,則會處理此錯誤並將其記錄在日誌中。如果連續拋出此錯誤,那麼核心運行狀況監視器線程將關閉 Weblogic Server。

2. 本地堆中的內存不足

如果JVM無法獲得更多本地內存,它將拋出本地內存不足(本地OOM)的錯誤。當進程到達操作系統的進程大小限值,或者當計算機用完 RAM 和交換空間時,通常會發生這種情況。

當發生這種情況時,JVM處理本地OOM狀態,記錄它已用完本地內存或無法獲得內存的消息,然後退出。如果JVM或使用本地內存的其它模塊不處理這個本地OOM狀態,那麼操作系統將給JVM發送命令JVM退出的sigabort信號。通常情況下,JVM收到sigabort信號時將會生成一個核心文件,並退出。


西湖小學網站信息發佈系統內存泄漏問題解決


一、 問題反饋

1. 系統背景

作爲示範用戶,西湖小學一直在使用網站等相關產品,公司對西湖小學的支持力度也比較大,2003年就定製了網站頁面,作爲示範網站進行宣傳,04年11月進行升級改版,到05年4月份完成改版定型升級工作,現使用網站發佈平臺V3.5版本,在杭州有較大的影響力。

2. 問題描述

05年4月新版本上馬後,一開始還比較穩定,大約兩週左右纔會出現網站不能訪問的情況。一個多月後網站崩潰週期變短,學校把內存加到2G,我們重新設置了內存參數後,一般一週需要重啓一次,後來隨着訪問量增大,越來越不穩定,需要兩天重啓一次。我們寫了一個bat,讓計劃任務每天凌晨自動重啓服務,但學校服務器禁止執行批處理。目前網站現象是每隔兩天,就會出現網站不能訪問,一般重啓就可以解決。

另外,用戶反應在系統後臺信息管理的信息添加模塊和類別添加模塊,最容易系統當機。

3. 用戶要求

用戶只要求系統當機的週期能長一些,最好在兩週以上即可。


二、 簡單分析

1. 用戶在2003年就開始使用信息發佈系統,而只有在升級到3.5新版後才發現不穩定現象;

2. 訪問量越大,系統當機時間縮短,當機的原因斷定是與計算機資源相關;

3. 服務重啓即可恢復正常,更加說明了是計算機資源問題,初步斷定是內存泄漏所致;

三、 學習與監控全過程

1. 服務器當機的原因是由於OOM錯誤所致,故對用戶服務器進行內存的定期監控。
由於一開始對一些工具使用的不熟悉,採用了人工監控;而後採用了windows自帶的監控工具:控制面板>管理工具> 性能(或perfmon命令)。

2. 網上搜索相關資料,並學習閱讀內存泄漏方面的文章。

3. 根據監測數據,初步認爲是內存使用量不斷增長所致。由於用戶服務器內存較大,初步緩解的解決方案是調整用戶服務器啓動參數,增大應用服務器的可用內存量。

4. 設置參數後,系統運行兩天左右仍存在當機現象,說明原因並未找到。

5. 測試部加入,準備採用Optimizeit專業檢測工具進行測試。在檢測工具使用前,先對測試環境進行了當機模擬的性能壓力測試。

6. 對比不同類別的OOM

通過設置啓動參數(-Xms -Xmx)的不同值所引發出來的OMM與在測試環境下產生的OOM略有不同,測試環境下的OOM有所不同,如下:
ava.lang.OutOfMemoryError
ava.lang.OutOfMemoryError: unable to create new native thread

7. 之後重心轉至“unable to create new native thread”上,再經過多次測試驗證,導致OOM的原因是由於JVM無法創建本地線程所致,而非實際意義上的內存不足問題上。

8. 找到原因 > 定位代碼 > 問題解決;

四、 小結
導致JAVA的OOM問題可能存在的原因會有很多,我們在解決該問題前應對它有總體的瞭解,並排除各種因素,最終定位原因所在。以個人經驗,原因主要有以下幾種:

1. JVM本身問題
2. 相關參數設置不合理
3. 程序中使用資源的未關閉
4. 一些編碼的不規範,對無用對象的引用
5. 海量數據的處理及緩存的使用

在對上述幾種原因分析之前,應有對一些基本知識和概念的瞭解,詳細見參考部分。另外,很重要的一點是:在發現OOM問題後,不要一味的認爲就是內存不足(即內存泄漏)所致,而忽略了其它了資源不足的可能,正確的做法是應先要以程序所報的錯誤信息爲依據,確定OOM類型,再做具體分析。在此次網站信息發佈系統內存泄漏問題解決過程中,就走了類似的彎路。

以下是對上述五種原因的簡單解決方案:
問題1
原因:此問題發生可能極小。
解決:一般掌握JVM拉圾回收(GC)的一些基礎知識和理論,很容易排除此類問題。
問題2
原因:內存使用設置太小。
解決:同問題1解決方案相似。
問題3
原因:此問題是程序開發過程中開發人員最易犯的錯誤,也是OOM查找過程中較難解決的問題。相關資源如數據庫連接、輸入輸出流、JMS消息發佈、webservice遠程調用等各種連接都存在資源關閉問題,若程序調用後不做關閉,可能發生不可預計的錯誤結果。

解決:對此類問題有兩個切入點:
1. 已經發生OOM的程序:此類程序只要通過人工或工具進行問題重現性的確認,即可定位相關源代碼並進行研讀,一般即可解決;
2. 未發生OOM或難以重現的程序:只有通過一些專業檢測工具進行檢測(如:Borland Optimizeit Profiler、JProbe Profiler 、JRockit Memory Leak Detector等)來進行檢測。難度較大,需對所使用的工具及相關基礎理論有較深入的掌握。
問題4
原因:通過編碼的不規範編寫出“對無用對象的引用”的代碼是存在可能的,但一般較難寫出類似的代碼;而對於問題3資源釋放的問題則是開發人員最易犯的一種錯誤。
解決:與問題3解決方案一樣。
問題5
原因:此類問題一般是在處理大批量數據時,沒有采用分批處理,而導致一次性申請過多內存,引發JVM內存使用量不足的OOM錯誤。如Hibernate的使用過程中,在同一個sesion裏,一次性從數據庫讀取海量數據,Hibernate的一級緩存並沒法釋放,則可能導致OOM錯誤。
解決:對此類問題有兩個切入點
1. 根本解決方案:對海量數據進行分批處理,讓一些內存對象得以回收;
2. 臨時解決方案:通過加大 %JAVA_OPTS% 啓動參數的內存使用量設置值,能有一定緩解能力。

http://www.edu88.com/emagazine/liaoyuan/detail.jsp?id=983&templateid=1
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章