重識JVM(三):OOM異常的常見處理思路

1.堆溢出

Java 堆用於存儲對象實例,只要不斷增加對象,並且保證GC Roots 到對象之間有可達路徑來避免垃圾回收機制清除這些對象,那麼在對象數量達到最大堆的容量限制後就會產生OOM 異常。

要解決這個區域的異常,一般的手段是通過內存映像分析工具對dump 出來的堆轉儲快照進行分析,重點是確認內存中的對象是否是必要的,也就是要判斷是出現來內存泄露(不必要)還是內存溢出(必要)。前者的話要進一步通過工具查看泄露對象到GC Roots 的引用鏈,就可以準確地定位出泄露代碼地位置;後者的話可以調大虛擬機的堆參數(-Xms 和-Xmx),或者從代碼上檢查是否存在某些對象生命週期過長的問題。

2.棧溢出(虛擬機棧和本地方法棧)

對於HotSpot 來說,雖然-Xoss 參數(設置本地方法棧大小)存在,但實際上是無效的,棧容量只由-Xss 參數設定。關於虛擬機棧和本地方法棧,在JVM 規範中描述了兩種異常:
1) 如果線程請求的棧深度大於虛擬機所允許的最大深度,將拋出StackOverflowError 異常。
2)如果虛擬機在擴展棧時無法申請到足夠的內存空間,將拋出OutOfMemoryError 異常。

操作系統分配給每個進程的內存是有限制的,每個線程分配到的棧容量越大,可以建立的線程數量自然就越少,建立線程時就越容易把剩下的內存耗盡。如果線程過多導致SOF,可以通過減少最大堆減少棧容量來換取更多的線程。

3.方法區溢出

注意Java8 下運行時常量池在堆中,所以運行時常量池過大會體現爲OOM:heap,而在此以前是放在永久代中,體現爲OOM:PermGen space。

方法區還存放Class 的相關信息,運行時產生大量的類也會導致方法區(Java8 中放在直接內存中)溢出。

4.直接內存溢出

直接內存可以使用-XX:MaxDirectMemorySize 指定,如果不指定,則默認與Java 堆最大值相同

雖然使用DirectByteBuffer 分配內存也會拋出OOM 異常,但它拋出異常時並沒有真正向OS申請分配內存,而是通過計算得知內存無法分配,於是手動拋出異常。真正申請內存的方法是unsafe.allocateMemory()。

5.內存泄露

1)非靜態內部類
2)連接未關閉:比如數據庫連接(dataSourse.getConnection()),網絡連接(socket)和io 連接,除非顯式的調用了其close()方法將其連接關閉,否則是不會自動被GC 回收的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章