【OOM】幾種常見的OOM異常


幾種常見的OOM異常

​ oom異常就是Out Of Memory Error 內存溢出異常,是我們開發中常見的異常。oom異常也分成多種。

java.lang.OutOfMemoryError: Java heap space 堆空間溢出,最常見的

在創建大對象的時候特別注意堆內存的使用,避免產生堆的內存溢出

模擬一下

/**
 * 堆內存溢出,這是工作中最常見的OOM故障
 * 1:在JVM啟動參數的時候將堆內存設置成10M  -Xmx10m -Xms10m
 */
public class HeapSpaceOomDemo {
    public static void main(String[] args) {
        //創建一個20M的對象
        Byte[] b = new Byte[20*1024*1024];
    }
}

控制台打印的結果:

stackoverflowError 棧空間溢出

模擬:

/**
 * OMM 之 java.lang. StackOverflowError 棧空間溢出,棧管運行,每個方法就是一個棧幀,循環調用方法,會出現這種問題
 */
public class StackOverFlowDemo {
    public static void main(String[] args) {
        stackoverflowError();
    }

    private static void stackoverflowError() {
        stackoverflowError();
    }
}

控制台結果:

GC overhead limit exceeded GC時間太長引發的異常

​ GC回收時間過長時會拋出outOfMemroyError,過長的定義是:超過98%的時間用來做GC並且回收不到2%的堆內存。
​ 連續多次GC都只回收了不到2%的極端情況下會拋出。假如不拋出GC overhead limit 錯誤會發生什么信況呢?
​ 那就是GC清理的這么點內存很快會再次填滿,迫使GC 再次執行。這樣就形成惡性循環,
CPU使用率一直是100%, 兩GC 卻沒有任何效果

​ 模擬:

/**
 * GC overhead limit exceeded
 * JVM參數設置:8U
 * -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
 */
public class OverheadLimitExceededDemo {
    public static void main(String[] args) {
        int i =0;
        final ArrayList list = new ArrayList();

        while (true){
            list.add(String.valueOf(++i).intern());
        }
    }
}

結果:

Directbuffer memory buffer 內存溢出,在

導致原因:

​ 寫NIO程序經常使ByteBuffer讀取或者寫入數據,這是一種基於通道(Channel)與緩沖區(Buffer)的I/0方式,

​ 它可以使用Native函數庫直接分配堆外內存,然后通過一個存儲在Java堆里面的DirectByteBuffer對象作為這塊內存的引用進行操作。

​ 這樣能在一些場景中顯著提高性能, 因為避免了在Java堆和Native堆中來回復制數據。
ByteBuffer. allocate(capability) 第一種方式是分配JVM堆內存, 屬於GC 營結范圍,由於需要拷貝所以速度相對較慢
​ ByteBuffer. allocteDirect(capability)第2種方式是分配操作系統本地內存,不屬於GC 管轄范圍,由於不需要內存拷貝所以速度相對較快。
​ 但如果不斷分配本地內存,堆內存很少使用,那么JVM就不需要執行CG, DirectByteBuffer對象們就不會被回收,
​ 這時候堆內存充足,但本地內存可能已經使用光了,再次嘗試分配本地內存就會出OutOfMemoryError,程序就直接崩潰。

模擬:

/**
 *  java.lang.OutOfMemoryError: Direct buffer memory 演示
 * JVM參數配置:-Xms10m -Xmx10m -XX:+PrintGCDetails  -XX:MaxDirectMemorySize=5m 
 */
public class DirectBufferMemoryDemo {
    public static void main(String[] args) {
        System.out.println("配置的maxDirectMemory: " + (sun.misc.VM.maxDirectMemory() / (double) 1024 / 1024) + "MB");
        //最大5M 申請6M
        ByteBuffer bb = ByteBuffer . allocateDirect(6* 1024*1024);

    }
}

結果:

unable to create new native thread 無法創建線程

​ 高並發請求服務器時,經常出現如下異常:java.Lang.OutOfMemoryError: unable to create new native thread准確的講native thread 異常與對應的平台有關

導致原因:

​ 你的應用創建了太多線程了,一個應用進程創建多個線程,超過系統承裁極限。
你的服務器並不允許你的應用程序創建這么多線程linux系統默認允許單個進程可以創建的線程數是1024個,你的應用創建超過這個數量,就會報java. lang. OutOfMemoryError: unable to create new native thread

解決辦法:

​ 1.想辦法降低你應用程序創建線程的數量,分析應用是否真的需要創建這么多線程如果不是,改代碼將線程數量降到最低
​ 2.對於有的應用,確實需要創建很多線程,遠超過Linux系統的默認1024個線程的限制,可以通過修改Linux服務器配置,擴大linux默認限制

模擬:這個類拷貝到linux下執行

/**
 * java.Lang.OutOfMemoryError: unable to create new native thread
 */
public class UnableCreateNewThreadDemo {
    public static void main(String[] args) {
        for (int i = 1;; i++) {
            System.out.println("i = " + i);
            new Thread(()->{
                try {
                    TimeUnit.SECONDS.sleep(MAX_VALUE);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },""+i).start();
        }
    }
}

java.lang.OutOfMemoryError: Metaspace 元空間溢出

​ Java 8及之后的版本使用Metaspace來替代永久化。Metaspace.是方法區在Hotspot中的實現,它與持久代最大的區別在於: Metaspace 不在虛擬機內存中而是使用本地內存
也即在java8中classe metadata(the virtual machines internal presentation of Java class), 被存儲在叫做Metaspace的native memory

永久代(java8后被原空間Metaspace取代了)存放了以下信息:
虛擬機加載的類信息
常量池
靜態變量
即時編譯后的代碼

模擬Metaspace空間溢出,我們不斷生成類往元空間灌,類占據的空間總是會超過Metaspace指定的空間大小的


/**
 * 異常演示: java.lang.OutOfMemoryError: Metaspace
 * -XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=8m
 */
class OomTest{}
public class MetaspaceDemo {
    public static void main(String[] args) {
        int i = 0;
        try {
            while (true){
                i++;
                final Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(OomTest.class);
                enhancer.setUseCache(false);
                enhancer.setCallback(new MethodInterceptor() {
                    @Override
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        return methodProxy.invoke(o,args);
                    }
                });

                enhancer.create();
            }
        }catch (Exception e){
            System.out.println("*****多少次發生異常 " + i);
            e.printStackTrace();
        }
    }
}

執行后的截圖:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM