異常、堆記憶體溢位、OOM的幾種情況

業餘草發表於2018-09-22

堆記憶體溢位

【情況一】: 
  java.lang.OutOfMemoryError: Java heap space:這種是java堆記憶體不夠,一個原因是真不夠,另一個原因是程式中有死迴圈; 
  如果是java堆記憶體不夠的話,可以通過調整JVM下面的配置來解決: 
  < jvm-arg>-Xms3062m < / jvm-arg> 
  < jvm-arg>-Xmx3062m < / jvm-arg> 
   
【情況二】 
  java.lang.OutOfMemoryError: GC overhead limit exceeded 
  【解釋】:JDK6新增錯誤型別,當GC為釋放很小空間佔用大量時間時丟擲;一般是因為堆太小,導致異常的原因,沒有足夠的記憶體。 
  【解決方案】: 
  1、檢視系統是否有使用大記憶體的程式碼或死迴圈; 
  2、通過新增JVM配置,來限制使用記憶體: 
  < jvm-arg>-XX:-UseGCOverheadLimit< /jvm-arg> 
   
【情況三】: 
  java.lang.OutOfMemoryError: PermGen space:這種是P區記憶體不夠,可通過調整JVM的配置: 
  < jvm-arg>-XX:MaxPermSize=128m< /jvm-arg> 
  < jvm-arg>-XXermSize=128m< /jvm-arg> 
  【注】: 
  JVM的Perm區主要用於存放Class和Meta資訊的,Class在被Loader時就會被放到PermGen space,這個區域成為年老代,GC在主程式執行期間不會對年老區進行清理,預設是64M大小,當程式需要載入的物件比較多時,超過64M就會報這部分記憶體溢位了,需要加大記憶體分配,一般128m足夠。 
   
【情況四】: 
  java.lang.OutOfMemoryError: Direct buffer memory 
  調整-XX:MaxDirectMemorySize= 引數,如新增JVM配置: 
  < jvm-arg>-XX:MaxDirectMemorySize=128m< /jvm-arg> 
   
【情況五】: 
  java.lang.OutOfMemoryError: unable to create new native thread 
  【原因】:Stack空間不足以建立額外的執行緒,要麼是建立的執行緒過多,要麼是Stack空間確實小了。 
  【解決】:由於JVM沒有提供引數設定總的stack空間大小,但可以設定單個執行緒棧的大小;而系統的使用者空間一共是3G,除了Text/Data/BSS /MemoryMapping幾個段之外,Heap和Stack空間的總量有限,是此消彼長的。因此遇到這個錯誤,可以通過兩個途徑解決: 
  1.通過 -Xss啟動引數減少單個執行緒棧大小,這樣便能開更多執行緒(當然不能太小,太小會出現StackOverflowError); 
  2.通過-Xms -Xmx 兩引數減少Heap大小,將記憶體讓給Stack(前提是保證Heap空間夠用)。 
   
【情況六】: 
  java.lang.StackOverflowError 
  【原因】:這也記憶體溢位錯誤的一種,即執行緒棧的溢位,要麼是方法呼叫層次過多(比如存在無限遞迴呼叫),要麼是執行緒棧太小。 
  【解決】:優化程式設計,減少方法呼叫層次;調整-Xss引數增加執行緒棧大小。

Java異常

Throwable 
Throwable是 Java 語言中所有錯誤或異常的超類。 
Throwable包含兩個子類: Error 和 Exception 。它們通常用於指示發生了異常情況。 
Throwable包含了其執行緒建立時執行緒執行堆疊的快照,它提供了printStackTrace()等介面用於獲取堆疊跟蹤資料等資訊。

Exception 
Exception及其子類是 Throwable 的一種形式,它指出了合理的應用程式想要捕獲的條件。

RuntimeException 
RuntimeException是那些可能在 Java 虛擬機器正常執行期間丟擲的異常的超類。 
編譯器不會檢查RuntimeException異常。 例如,除數為零時,丟擲ArithmeticException異常。RuntimeException是ArithmeticException的超類。當程式碼發生除數為零的情況時,倘若既”沒有通過throws宣告丟擲ArithmeticException異常”,也”沒有通過try…catch…處理該異常”,也能通過編譯。這就是我們所說的”編譯器不會檢查RuntimeException異常”! 
如果程式碼會產生RuntimeException異常,則需要通過修改程式碼進行避免。 例如,若會發生除數為零的情況,則需要通過程式碼避免該情況的發生!

Error 
和Exception一樣, Error也是Throwable的子類。 它用於指示合理的應用程式不應該試圖捕獲的嚴重問題,大多數這樣的錯誤都是異常條件。 
和RuntimeException一樣, 編譯器也不會檢查Error。

Java將可丟擲(Throwable)的結構分為三種型別: 被檢查的異常(Checked Exception),執行時異常(RuntimeException)和錯誤(Error)。

(01) 執行時異常 
定義 : RuntimeException及其子類都被稱為執行時異常。 
特點 : Java編譯器不會檢查它。 也就是說,當程式中可能出現這類異常時,倘若既”沒有通過throws宣告丟擲它”,也”沒有用try-catch語句捕獲它”,還是會編譯通過。例如,除數為零時產生的ArithmeticException異常,陣列越界時產生的IndexOutOfBoundsException異常,fail-fail機制產生的ConcurrentModificationException異常等,都屬於執行時異常。 
雖然Java編譯器不會檢查執行時異常,但是我們也可以通過throws進行宣告丟擲,也可以通過try-catch對它進行捕獲處理。 
如果產生執行時異常,則需要通過修改程式碼來進行避免。 例如,若會發生除數為零的情況,則需要通過程式碼避免該情況的發生!

(02) 被檢查的異常 
定義 : Exception類本身,以及Exception的子類中除了”執行時異常”之外的其它子類都屬於被檢查異常。 
特點 : Java編譯器會檢查它。 此類異常,要麼通過throws進行宣告丟擲,要麼通過try-catch進行捕獲處理,否則不能通過編譯。例如,CloneNotSupportedException就屬於被檢查異常。當通過clone()介面去克隆一個物件,而該物件對應的類沒有實現Cloneable介面,就會丟擲CloneNotSupportedException異常。 
被檢查異常通常都是可以恢復的。

(03) 錯誤 
定義 : Error類及其子類。 
特點 : 和執行時異常一樣,編譯器也不會對錯誤進行檢查。 
當資源不足、約束失敗、或是其它程式無法繼續執行的條件發生時,就產生錯誤。程式本身無法修復這些錯誤的。例如,VirtualMachineError就屬於錯誤。 
按照Java慣例,我們是不應該是實現任何新的Error子類的!

對於上面的3種結構,我們在丟擲異常或錯誤時,到底該哪一種?《Effective Java》中給出的建議是: 對於可以恢復的條件使用被檢查異常,對於程式錯誤使用執行時異常。

OOM

1, OutOfMemoryError異常

除了程式計數器外,虛擬機器記憶體的其他幾個執行時區域都有發生OutOfMemoryError(OOM)異常的可能,

Java Heap 溢位

一般的異常資訊:java.lang.OutOfMemoryError:Java heap spacess

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

出現這種異常,一般手段是先通過記憶體映像分析工具(如Eclipse Memory Analyzer)對dump出來的堆轉存快照進行分析,重點是確認記憶體中的物件是否是必要的,先分清是因為記憶體洩漏(Memory Leak)還是記憶體溢位(Memory Overflow)。

如果是記憶體洩漏,可進一步通過工具檢視洩漏物件到GC Roots的引用鏈。於是就能找到洩漏物件時通過怎樣的路徑與GC Roots相關聯並導致垃圾收集器無法自動回收。

如果不存在洩漏,那就應該檢查虛擬機器的引數(-Xmx與-Xms)的設定是否適當。

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

如果執行緒請求的棧深度大於虛擬機器所允許的最大深度,將丟擲StackOverflowError異常。

如果虛擬機器在擴充套件棧時無法申請到足夠的記憶體空間,則丟擲OutOfMemoryError異常

這裡需要注意當棧的大小越大可分配的執行緒數就越少。

3, 執行時常量池溢位

異常資訊:java.lang.OutOfMemoryError:PermGen space

如果要向執行時常量池中新增內容,最簡單的做法就是使用String.intern()這個Native方法。該方法的作用是:如果池中已經包含一個等於此String的字串,則返回代表池中這個字串的String物件;否則,將此String物件包含的字串新增到常量池中,並且返回此String物件的引用。由於常量池分配在方法區內,我們可以通過-XX:PermSize和-XX:MaxPermSize限制方法區的大小,從而間接限制其中常量池的容量。

4, 方法區溢位

方法區用於存放Class的相關資訊,如類名、訪問修飾符、常量池、欄位描述、方法描述等。

異常資訊:java.lang.OutOfMemoryError:PermGen space

方法區溢位也是一種常見的記憶體溢位異常,一個類如果要被垃圾收集器回收,判定條件是很苛刻的。在經常動態生成大量Class的應用中,要特別注意這點。

感謝您的關注!可加QQ1群:135430763,QQ2群:454796847,QQ3群:187424846。QQ群進群密碼:xttblog,想加微信群的朋友,可以微信搜尋:xmtxtt,備註:“xttblog”,新增助理微信拉你進群。備註錯誤不會同意好友申請。再次感謝您的關注!後續有精彩內容會第一時間發給您!原創文章投稿請傳送至532009913@qq.com郵箱。商務合作可新增助理微信進行溝通! 

相關文章