1. 程式人生 > >Java 虛擬機器記憶體溢位問題和解決方法

Java 虛擬機器記憶體溢位問題和解決方法

一什麼是記憶體溢位

1記憶體溢位是指應用系統中存在無法回收的記憶體或使用的記憶體過多,最終使得程式執行要用到的記憶體大於虛擬機器能提供的最大記憶體。
2 Java的記憶體管理就是物件的分配和釋放問題。
在Java中,記憶體的分配是由程式完成的,而記憶體的釋放是由垃圾收集器(Garbage Collection,GC)完成的,程式設計師不需要通過呼叫GC函式來釋放記憶體,因為不同的JVM實現者可能使用不同的演算法管理GC,有的是記憶體使用到達一定程度時,GC才開始工作,也有定時執行的,有的是中斷式執行GC。但GC只能回收無用並且不再被其它物件引用的那些物件所佔用的空間。Java的記憶體垃圾回收機制是從程式的主要執行物件開始檢查引用鏈,當遍歷一遍後發現沒有被引用的孤立物件就作為垃圾回收。

二記憶體溢位的原因:

1記憶體中載入的資料量過於龐大,如一次從資料庫取出過多資料;
2集合類中有對物件的引用,使用完後未清空,使得JVM不能回收;
3程式碼中存在死迴圈或迴圈產生過多重複的物件實體;
4使用的第三方軟體中的BUG;
5啟動引數記憶體值設定的過小;

三解決辦法

1修改JVM啟動引數,直接增加記憶體。
JVM預設可以使用的記憶體為64M,Tomcat預設可以使用的記憶體為128MB。這對於其他大型引用絕對是不夠的
2檢查錯誤日誌,檢視“OutOfMemory”錯誤前是否有其它異常或錯誤。

3對程式碼進行走查和分析,
檢查程式碼中是否有死迴圈或遞迴呼叫。
檢查是否有大迴圈重複產生新物件實體。
檢查對資料庫查詢中,是否有一次獲得全部資料的查詢,而沒有使用分頁。
檢查List、MAP等集合物件是否有使用完後,未清除的問題。List、MAP等集合物件會始終存有對物件的引用,使得這些物件不能被GC回收。
4、儘早釋放無用物件的引用。好的辦法是使用臨時變數的時候,讓引用變數在退出活動域後,自動設定為 null ,暗示垃圾收集器來收集該物件,防止發生記憶體洩露。
對於仍然有指標指向的例項, jvm 就不會回收該資源 , 因為垃圾回收會將值為 null 的物件作為垃圾,提高 GC 回收機制效率;
5 、我們的程式裡不可避免大量使用字串處理,避免使用 String ,應大量使用 StringBuffer ,每一個 String 物件都得獨立佔用記憶體一塊區域;
String str = “aaa”;

String str2 = “bbb”;

String str3 = str + str2;// 假如執行此次之後 str ,str2 以後再不被呼叫 , 那它就會被放在記憶體中等待 Java 的 gc 去回收 , 程式內過多的出現這樣的情況就會報上面的那個錯誤 ,

6 、儘量少用靜態變數,因為靜態變數是全域性的, GC 不會回收的;

7 、避免集中建立物件尤其是大物件, JVM 會突然需要大量記憶體,這時必然會觸發 GC 優化系統記憶體環境;顯示的宣告陣列空間,而且申請數量還極大。
比如:
有個excel檔案上傳的功能,excel內容有非常大,每次上傳都導致jvm需要大量的記憶體。

8 、儘量運用物件池技術以提高系統性能;生命週期長的物件擁有生命週期短的物件時容易引發記憶體洩漏,例如大集合物件擁有大資料量的業務物件的時候,可以考慮分塊進行處理,然後解決一塊釋放一塊的策略。
9 、不要在經常呼叫的方法中建立物件,尤其是忌諱在迴圈中建立物件。可以適當的使用 hashtable , vector 建立一組物件容器,然後從容器中去取那些物件,而不用每次 new 之後又丟棄
10 、一般都是發生在開啟大型檔案或跟資料庫一次拿了太多的資料,造成 Out Of Memory Error的狀況,這時就大概要計算一下資料量的最大值是多少,並且設定所需最小及最大的記憶體空間值。

四使用記憶體檢視工具動態檢視記憶體使用情況

1記憶體檢視工具有許多,比較有名的有:Optimizeit Profiler、JProbe Profiler、JinSight和Java1.5的Jconsole等。它們的基本工作原理大同小異,都是監測Java程式執行時所有物件的申請、釋放等動作,將記憶體管理的所有資訊進行統計、分析、視覺化。開發人員可以根據這些資訊判斷程式是否有記憶體洩漏問題。一般來說,一個正常的系統在其啟動完成後其記憶體的佔用量是基本穩定的,而不應該是無限制的增長的。持續地觀察系統執行時使用的記憶體的大小,可以看到在記憶體使用監控視窗中是基本規則的鋸齒形的圖線,如果記憶體的大小持續地增長,則說明系統存在記憶體洩漏問題。通過間隔一段時間取一次記憶體快照,然後對記憶體快照中物件的使用與引用等資訊進行比對與分析,可以找出是哪個類的物件在洩漏。

五記憶體溢位型別

1 java.lang.OutOfMemoryError: PermGen space
1JVM 管理兩種型別的記憶體,堆和非堆。
2堆是給開發人員用的 ;
3非堆是留給 JVM 自己用的,用來存放類的資訊的,執行期內 GC 不會釋放空間。
如果 web app 用了大量的第三方 jar 或者應用有太多的 class 檔案而恰好 MaxPermSize 設定較小,超出了也會導致這塊記憶體的佔用過多造成溢位,或者 tomcat 熱部署時侯不會清理前面載入的環境,只會將 context 更改為新部署的,非堆存的內容就會越來越多。

2 java.lang.OutOfMemoryError: Java heap space
其預設空間 ( 即 -Xms) 是實體記憶體的 1/64 ,最大空間 (-Xmx) 是實體記憶體的 1/4 。如果記憶體剩餘不到 40 %, JVM 就會增大堆到 Xmx 設定的值,記憶體剩餘超過 70 %, JVM 就會減小堆到 Xms 設定的值。所以伺服器的 Xmx 和 Xms 設定一般應該設定相同避免每次 GC 後都要調整虛擬機器堆的大小。假設實體記憶體無限大,那麼 JVM 記憶體的最大值跟作業系統有關,一般 32 位機是 1.5g 到 3g 之間,而 64 位的就不會有限制了。
注意:如果 Xms 超過了 Xmx 值,或者堆最大值和非堆最大值的總和超過了實體記憶體或者作業系統的最大限制都會引起伺服器啟動不起來。

六垃圾回收 GC 的角色

1當應用程式執行緒空閒,呼叫 GC。
2java 記憶體堆不足時,會不斷呼叫 GC ,若連續回收都解決不了記憶體堆不足的問題時,就會報 out of memory
3 根據 GC 的機制,程式的執行會引起系統執行環境的變化,增加 GC 的觸發機會。
建議:
程式的設計和編寫應避免垃圾物件的記憶體佔用和 GC 的開銷。顯示呼叫 System.GC() 只能建議 JVM 需要在記憶體中對垃圾物件進行回收,但不是必須馬上回收,
並不能解決記憶體資源耗空的局面,另外也會增加 GC 的消耗。

七 JVM 記憶體區域組成

java把記憶體分兩種:一種是棧記憶體,另一種是堆記憶體
1棧記憶體:
存放函式中定義的基本型別變數和物件的引用變數
2堆記憶體:
存放由 new建立的物件和陣列

在函式(程式碼塊)中定義一個變數時, java就在棧中為這個變數分配記憶體空間,當超過變數的作用域後, java會自動釋放掉為該變數所分配的記憶體空間;在堆中分配的記憶體由 java虛擬機器的自動垃圾回收器來管理
優缺點
堆的優勢是可以動態分配記憶體大小,生存期也不必事先告訴編譯器,因為它是在執行時動態分配記憶體的。缺點就是要在執行時動態分配記憶體,存取速度較慢;
棧的優勢是存取速度比堆要快,缺點是存在棧中的資料大小與生存期必須是確定的無靈活 性。
java 堆分為三個區: New 、 Old 和 Permanent
GC 有兩個執行緒:
新建立的物件被分配到 New 區,當該區被填滿時會被 GC 輔助執行緒移到 Old 區,當 Old 區也填滿了會觸發 GC 主執行緒遍歷堆記憶體裡的所有物件。 Old 區的大小等於 Xmx 減去 -Xmn
棧調整:引數有 +UseDefaultStackSize -Xss256K,表示每個執行緒可申請 256k的棧空間
每個執行緒都有他自己的 Stack

八、 JVM如何設定虛擬記憶體

在 JVM中如果 98%的時間是用於 GC且可用的 Heap size 不足 2%的時候將丟擲此異常資訊。

1.提示: Heap Size 最大不要超過可用實體記憶體的 80%,一般的要將 -Xms和 -Xmx選項設定為相同,而 -Xmn為 1/4的 -Xmx值。
2.提示: JVM初始分配的記憶體由 -Xms指定,預設是實體記憶體的 1/64; JVM最大分配的記憶體由 -Xmx指定,預設是實體記憶體的 1/4。
3預設空餘堆記憶體小於 40%時, JVM就會增大堆直到 -Xmx的最大限制;空餘堆記憶體大於 70%時, JVM會減少堆直到 -Xms的最小限制。因此伺服器一般設定 -Xms、 -Xmx相等以避免在每次 GC 後調整堆的大小。
4.提示:假設實體記憶體無限大的話, JVM記憶體的最大值跟作業系統有很大的關係。
簡單的說就 32位處理器雖然可控記憶體空間有 4GB,但是具體的作業系統會給一個限制,
這個限制一般是 2GB-3GB(一般來說 Windows系統下為 1.5G-2G, Linux系統下為 2G-3G), 而 64bit以上的處理器就不會有限制了
5.提示:注意:如果 Xms超過了 Xmx值,或者堆最大值和非堆最大值的總和超過了物理內 存或者作業系統的最大限制都會引起伺服器啟動不起來。
6.提示:設定 NewSize、 MaxNewSize相等, “new”的大小最好不要大於 “old”的一半,原因是 old區如果不夠大會頻繁的觸發 “主 ” GC ,大大降低了效能
JVM使用 -XX:PermSize設定非堆記憶體初始值,預設是實體記憶體的 1/64;
由 XX:MaxPermSize設定最大非堆記憶體的大小,預設是實體記憶體的 1/4。
解決方法:手動設定 Heap size
修改 TOMCAT_HOME/bin/catalina.bat
在“ echo “Using CATALINA_BASE: $CATALINA_BASE””上面加入以下行:
JAVA_OPTS=”-server -Xms800m -Xmx800m -XX:MaxNewSize=256m”

九記憶體溢位現象

  1. 應用伺服器記憶體長期不合理佔用,記憶體經常處於高位佔用,很難回收到低位;
  2. 應用伺服器極為不穩定,幾乎每兩天重新啟動一次,有時甚至每天重新啟動一次;
  3. 應用伺服器經常做 Full GC(Garbage Collection),而且時間很長,大約需要 30-40秒,應用伺服器在做 Full GC的時候是不響應客戶的交易請求的,非常影響系統性能。