這篇文章帶給大家的內容是關於Java異常之OutOfMemoryError的解決方法,有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
在Java虛擬機器規格描述中,除了程式計數器外,虛擬機器記憶體的其他幾個運行區域都有發生 OOM 異常的可能。在這裡,用程式碼驗證各個運行時區域儲存的內容並討論該如何進行處理。
Java堆溢位
Java 堆用於儲存物件實例,只要不斷建立對象,並且保證GC Roots 到物件之間有可達路徑來避免垃圾回收機制清除這些對象,那麼對象數量達到最大堆的容量限制之後就會產生記憶體溢位異常。
異常再現
程式碼採用以下虛擬機器參數:
-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
這樣 Java 堆的大小將被限制為20 MB 且不可拓展。透過參數-XX: HeapDumpOnOutOfMemoryError可以讓虛擬機器在出現記憶體溢出異常時 Dump 出目前的記憶體堆轉儲快照以便時候進行分析。
採用以下程式碼進行驗證:
public class HeapOOM { static class OOMObject { } public static void main(String[] args) { Listlist = new ArrayList (); while (true) { list.add(new OOMObject()); } } }
運行結果:
java.lang.OutOfMemoryError: Java heap space Dumping heap to java_pid3460.hprof ... Heap dump file created [28199779 bytes in 0.237 secs]
Java 堆記憶體的OOM 異常是實際應用中常見的內存溢出異常情況,出現時往往會緊跟著提示「Java heap space」。
要解決這個區域的異常,一般的手段是先透過記憶體映像分析工具,例如 MAT ,確認到底是出現了記憶體洩漏還是記憶體溢位。
如果是記憶體洩漏,可以進一步透過工具查看洩漏物件到GC Roots 的參考鏈,找到洩漏物件是透過怎樣的途徑和GC Roots 相關聯並導致垃圾收集器無法自動回收它們所佔的空間。
如果不是記憶體洩漏,換而言之,記憶體中的物件確實還有必要存活著,那麼就應當檢查虛擬機的堆參數,與機器物理記憶體對比看是否還可以調大。從程式碼層面來看,是否存在某些物件生命週期過長、持有狀態時間過長的情況,嘗試減少程式運行期間的記憶體消耗。
由於在HotSpot 虛擬機中並不區分虛擬機堆疊或本機方法棧,因此對於HotSpot 而言,雖然-Xoss 參數存在,但是其實是無效的,堆疊容量只由-Xss 參數設定。
在單執行緒下,程式碼採用如下的虛擬機器參數:
-Xss128k
使用此參數減小堆疊容量,使用下列程式碼複現例外:
public class JavaVMStackSOF { private int stackLength = 1; public void stackLeak() { stackLength++; stackLeak(); } public static void main(String[] args) throws Throwable { JavaVMStackSOF oom = new JavaVMStackSOF(); try { oom.stackLeak(); } catch (Throwable e) { System.out.println("stack length:" + oom.stackLength); throw e; } } }
如果使用虛擬機器預設參數,堆疊深度在大多數情況下(因為每個方法壓入堆疊的幀大小並不是一樣的,所以只能說在大多數情況下)達到1000 ~ 2000 完全沒有問題,對於正常的方法呼叫(包括遞歸),這個深度應該完全足夠。
但是,如果是因為建立過多的執行緒導致記憶體溢出,在不能減少執行緒數或更換64位元虛擬機的情況下,就只能透過減少最大堆和減少堆疊容量來換取更多的線程。
本機直接記憶體溢位
DirectMemory 容量可以透過-XX :MaxDirectMemorySize指定,如果不指定,則預設與Java最大堆一樣。
使用下列虛擬機器參數:
-Xmx20M -XX:MaxDirectMemorySize=10M
使用下列程式碼重現例外:
public class DirectMemoryOOM { private static final int _1MB = 1024 * 1024; public static void main(String[] args) throws Exception { Field unsafeField = Unsafe.class.getDeclaredFields()[0]; unsafeField.setAccessible(true); Unsafe unsafe = (Unsafe) unsafeField.get(null); while (true) { unsafe.allocateMemory(_1MB);//直接申请分配内存 } } }
由DirectMemory 導致的記憶體溢出,一個明顯的特徵就是在Heap Dump 檔案中不會看見明顯的異常。
如果發現 OOM 之後Dump檔案很小,而程式中又直接或間接使用了NIO ,那麼就可以考慮檢查一下是不是這方面的原因。
以上是Java異常之OutOfMemoryError的解決方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!