JVM(四)——OutOfMemoryError 異常

zhoupq發表於2017-04-06

JVM(四)——OutOfMemoryError 異常


JVM(四)——OutOfMemoryError 異常
  在 Java 執行時的資料區域 中我已經知道了丟擲 OutOfMemoryError 異常的區域在 虛擬機器棧、本地方法棧、java堆和方法區幾個區域。

Java 堆溢位

  Java 堆是建立物件的區域,當GC來不及清除這些物件,並且物件數量達到最大堆的容量限制後就會產生記憶體溢位異常。

  Java 堆可以自動擴充套件,如果將 -Xms(堆最小值)和 -Xmx(堆最大值)設定為一樣可以避免堆自動擴充套件。

  Java 堆記憶體的 OOM 異常是實際應用中常見的記憶體溢位情況。當 Java 堆出現 OOM 異常時,會提示錯誤資訊:

java.lang.OutOfMemoryError: Java heap sapce複製程式碼

  值得注意的是,此時異常還分為兩種:

  • 記憶體洩露(Memroy Leak)
      物件本應該被 GC 回收,而未被回收。記憶體從 GC 中洩露出來。
  • 記憶體溢位(Memory Overflow)
      物件都還必須存活(JVM會判斷),這些物件佔的記憶體超出了Java堆的最大容量。此時最直接的方法就是調大實體記憶體。

  兩者的唯一區別就是,GC沒有清除的物件是否是必要的存在的。

虛擬機器棧

  由於在HotSpot虛擬機器中並不區分虛擬機器棧和本地方法棧,所以,-Xoss引數(本地方法棧大小)沒有是實際作用。棧容量只由 -Xss 引數設定

  在虛擬機器棧中,存在兩種異常:

  • SatckOverflowError
      執行緒請求的站深度大於虛擬機器棧所允許的最大深度
  • OutOfMemoryError
      虛擬機器在擴充套件時無法申請到足夠的記憶體空間(同java堆)

  當在單執行緒環境中,只會出現 StackOverflowError 異常。切換至多執行緒後,會出現 OutOfMemoryError 異常。

  看起來,可以通過增大記憶體空間來緩解或者解決虛擬機器棧的 OOM,但是,通過 Java 執行時的資料區域 分析得知:

物理機總記憶體 = 程式計數器消耗記憶體 + Xmx(最大 Java堆容量)+ MaxPermSize(最大方法區容量)+ Xss(虛擬機器棧容量)+ Xoss(本地棧容量)複製程式碼

  由於程式計數器消耗記憶體很小,忽略不計。並且將本地棧容量歸為虛擬機器棧容量管理的話,那麼:

Xss(虛擬機器棧容量)= 物理機總記憶體 -  Xmx(最大 Java堆容量)- MaxPermSize(最大方法區容量)複製程式碼

  虛擬機器棧是執行緒的“私有記憶體”,目的是執行緒分配執行時需要的記憶體容量,所以有:

Xss(虛擬機器棧容量)= 執行緒數 * 執行緒棧容量複製程式碼

  最後得到:

執行緒數 * 執行緒棧容量 = 物理機總記憶體 -  Xmx(最大 Java堆容量)- MaxPermSize(最大方法區容量)複製程式碼

  在實際情況中,我們希望得到更高的併發量,也就是增大執行緒數量,此時,有三種方法:

  • 增大物理機總記憶體
  • 減小java堆記憶體、方法區容量
  • 減小執行緒棧容量

  作為開發人員,能做的,也就是後兩者。第一條不說申請流程複雜,就算是增加了,那也可能是個無底洞。

相關文章