1. 程式人生 > >Jvm中記憶體溢位的4種情況

Jvm中記憶體溢位的4種情況

1、java堆溢位
java對用於儲存物件的例項,只要不斷的建立物件,並且保證GC Roots到物件之間有可達路徑來避免垃圾回收機制清除這些物件,那麼在物件數量達到最大堆的容量限制之後機會產生記憶體溢位異常。

當出現java堆記憶體溢位時,異常堆疊資訊“java.lang.OutOfMemoryError”會跟著進一步提示“Java heap space”。
要解決這個區域的異常 ,一般會先通過記憶體映像分析工具對Dump出來的對轉儲存快照盡心分析,重點是確認記憶體中的物件是否是必要的,也就是先分清楚到底是記憶體洩漏(Memory Leak)還是記憶體溢位(Memory Overflow)。

若果是記憶體洩漏,可進一步通過工具檢視洩漏物件到GC Roots的引用鏈。於是就能找到洩漏物件是通過怎樣的路徑與GC Roots相關並導致垃圾回收器無法自動回收他們。如果不存在洩漏,就是記憶體中的物件確實都還必須活著,那就應當檢查虛擬機器的堆引數(-Xmx和Xms),與機器實體記憶體對比是否還可以調大,從程式碼上檢查是否存在某些物件生命週期過長,持有狀態時間過長的情況,嘗試減少程式執行期的記憶體消耗。

2、虛擬機器棧和本地方法棧溢位

如果執行緒請求的棧深度大於虛擬機器所允許的最大深度,將會丟擲StackOverflowError。如果虛擬機器在擴充套件棧時無法申請到足夠的記憶體空間則丟擲OutOfMemoryError。
以上兩種異常情況其實是對同一件事情的兩種描述:當棧空間無法繼續分配時,到底是棧記憶體太小,還是已經使用的棧空間太大。

試驗表明:在單個執行緒下,無論是由於棧幀太大還是虛擬機器棧容量太小,當記憶體無法分配的時候,虛擬機器丟擲的StackOverflowError異常。如果不限於單執行緒,通過不斷的建立執行緒的方式到可以產生記憶體溢位異常,但是這樣產生的記憶體溢位異常與棧空間是否足夠大並不存在任何聯絡,或者更準確的說,在這種情況下,為每一個執行緒的棧分配的記憶體越大,反而越容易產生記憶體溢位異常。

如果是通過建立過多執行緒的方式到導致的記憶體溢位,在不能減少執行緒或者更換64位虛擬機器的情況下,就只能通過減少最大堆和減少棧幀容量來換取跟多的執行緒。

原因很簡單:作業系統分配給每一個程序的記憶體是有限制的,比如每個對程序的限制為2GB,虛擬機器提供了引數來控制java堆和方法區的這兩部分記憶體的最大值。則剩餘的記憶體為:2GB(作業系統限制的大小)-Xmx(最大堆容量)-MaxPermSize(最大方法區容量)。程式計數器消耗的記憶體很小基本可以忽略。如果虛擬機器程序本身消耗的記憶體不計算在內,那麼剩下的記憶體就由虛擬機器的虛擬機器棧和本地方法棧“瓜分”了。每個執行緒分配到的記憶體容量越大,可以建立的執行緒數量自然就越少,建立執行緒時就越容易將剩下的記憶體耗盡。

3、方法區和執行時常量池溢位

若是執行時常量池溢位,在OutOfMemoryError後面跟隨的提示資訊是“PermGen space”,說明執行時常量池屬於方法區(永久代)的一部分。

方法區用於存放Class的相關資訊,如類名,訪問修飾符,常量池,欄位描述,方法描述。若執行時產生大量的類去填滿方法區,直達溢位。比如Spring 對類進行增強時(建立代理類),都會使用到cglib這類位元組碼技術,增強的類越多,就需要越大的方法區來保證動態生成的Class可以載入到記憶體。再比如大量JSP或動態產生JSP檔案應用(JSP第一次執行時需要編譯為java類)等。

4、本機直接記憶體溢位
DirectMenory容量可通過-XX:MaxDirectMemorySize指定,如果不指定,則預設與java堆的容量最大值一樣。