首頁 > Java > java教程 > 主體

Java異常之OutOfMemoryError的解決方法

不言
發布: 2019-01-30 11:08:17
轉載
3181 人瀏覽過

這篇文章帶給大家的內容是關於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) {
        List<OOMObject> list = new ArrayList<OOMObject>();

        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中文網其他相關文章!

相關標籤:
來源:cnblogs.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板