1. 程式人生 > >Java記憶體溢位錯誤:OutOfMemoryError異常分析

Java記憶體溢位錯誤:OutOfMemoryError異常分析

JVM的執行時資料區,除了程式計數器之外,其他區域都有可能會產生OutOfMemoryError異常。

Java堆溢位

        Java堆溢位時會報下面的異常錯誤:

java.lang.OutOfMemoryError: Java heap space

在啟動虛擬機器的時候可以加上引數:-XX:+HeapDumpOnOutOfMemoryError,讓虛擬機器在出現記憶體溢位異常時Dump出當前的記憶體堆轉儲快照。出現堆溢位後,可以通過記憶體映像分析工具(如Eclipse Memory Analyzer)對堆轉存快照進行分析:

首先要分析清楚到底是記憶體洩露(Memory Leak

)還是記憶體溢位(Memory Overflow)。

如果是記憶體洩露,說明物件無法被GC回收。通過工具可以檢視物件的引用鏈,檢查在引用鏈的路徑中是否有不正確的使用導致無法被GC回收。

如果不是記憶體洩露,物件確實還必須存活,就需要檢查虛擬機器的引數(-Xmx,-Xms)和物理機記憶體是否可以調大;程式碼中檢查部分物件是否生命週期過長等情況。

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

Hotspot虛擬機器中不區分虛擬機器棧和本地方法棧,棧容量都由引數-Xss來設定。在棧溢位的規定中有兩種異常:

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

2、如果虛擬機器在擴充套件棧時無法申請到足夠的空間,將丟擲OutOfMemoryError

不過在實際實驗中,在單執行緒下,無論是棧幀太大,還是虛擬機器棧容量太小,當記憶體無法分配的時候,都是丟擲StackOverflowError。而在多執行緒下,執行緒過多耗盡記憶體則丟擲OutOfMemoryError異常。

假設作業系統有剩餘記憶體2G,減去Xmx(最大堆容量),再減去MaxPermSize(最大方法區容量),程式計數器消耗很少可忽略,不算虛擬機器本身消耗的記憶體,剩下的就是棧(虛擬機器棧+本地方法棧)的記憶體了。剩下的記憶體就這麼大,每個執行緒的棧容量越大,可建立的執行緒就越少。這時把記憶體耗盡就會產生OutOfMemoryError異常。

java.lang.OutOfMemoryError: unable to create new native thread

由於多執行緒導致的記憶體溢位,在不能減少執行緒數或者增加系統記憶體的情況下,只能通過減少最大堆和減少棧容量來換取更多執行緒。

上述異常發生可以有以下幾個方向解決:

  •  最根本的問題是,是否真的需要這麼多的執行緒,能否使用執行緒池來控制執行緒數量
  • 增加系統記憶體(程序限制的最大記憶體),如使用64位系統、修改執行緒限制
  • 減小棧容量,JVM預設1024k的棧容量,一般不需要這麼大,使用-Xss來修改
  • 減小堆大小,使棧能從程序中得到的記憶體增大,用-Xmx引數來修改
  • 減小方法區大小,原理同上,用-XX:MaxPermSize引數修改

執行時常量池溢位

執行時常量池屬於方法區(Hotspot虛擬機器的持久代),可以通過-XX:MaxPermSize-XX:PermSize引數來設定持久代大小。所以常量池溢位就是丟擲OutOfMemoryError異常並說明是在持久代記憶體溢位了。

java.lang.OutOfMemoryError: PermGen space

方法區溢位

方法區存放Class的相關資訊,通過常量池的情況可以知道,方法區在Hotspot虛擬機器的持久代,溢位時丟擲的溢位和常量池是一樣的

java.lang.OutOfMemoryError: PermGen space

本機直接記憶體溢位

直接記憶體可以通過-XX:MaxDirectMemorySize指定,預設是與-Xmx一樣大。DirectByteBuffer類可以從本機記憶體來分配,丟擲記憶體溢位只顯示OutOfMemoryError異常:

java.lang.OutOfMemoryError