【OOM】Java虛擬機常見內存溢出錯誤

1、引言

從事java開發的小夥伴在平時的開發工做中,應該會碰見各式各樣的異常和錯誤,在實際工做中積累的異常或者錯誤越多,趟過的坑越多,就會使咱們編碼更加的健壯,就會本能地避開不少嚴重的坑。如下介紹幾個Java虛擬機常見內存溢出錯誤。以此警示,避免生產血案。java

2、模擬Java虛擬機常見內存溢出錯誤

一、內存溢出之棧溢出錯誤

package com.jayway.oom;  
  
/**  
 * 棧溢出錯誤  
 * 虛擬機參數:-Xms10m -Xmx10m  
 * 拋出異常:Exception in thread "main" java.lang.StackOverflowError  
 */
 public class StackOverflowErrorDemo {  
  
 public static void main(String[] args) {  
    stackOverflowError();  
 }  
  
 private static void stackOverflowError() {  
    stackOverflowError();  
 }  
  
}

二、內存溢出之堆溢出錯誤

package com.jayway.oom;  
  
import java.util.Random;  
  
/**  
 * 堆溢出錯誤  
 * 虛擬機參數:-Xmx10m -Xms10m
 * 拋出異常:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space  
 */
 public class JavaHeapSpaceErrorDemo {  
  
 public static void main(String[] args) {  
    String temp = "java";  
    //不斷地在堆中開闢空間,建立對象,撐爆堆內存
    while (true) {  
        temp += temp + new Random().nextInt(111111111) + new Random().nextInt(222222222);  
        temp.intern();  
    }  
 }  
}

三、內存溢出之GC超過執行限制錯誤

package com.jayway.oom;  
  
import java.util.ArrayList;  
import java.util.List;  
  
/**  
 * GC超過執行限制錯誤  
 * 虛擬機參數:-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m  
 * * 拋出異常:Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded  
 * * 致使緣由:GC回收時間過長會拋出OutOfMemoryError,何爲過長,即超過98%的cpu時間用來作GC垃圾回收  
 * 可是回收效果甚微,僅僅只有2%的CPU時間用來用戶程序的工做,這種狀態是很糟糕的,程序在不斷地GC  
 * 造成惡性循環,CPU的使用率一直是滿負荷的,正經活卻沒有幹,這種狀況虛擬機只好拋出錯誤來終止程序的執行  
 *  
 * 不斷地Full GC,事倍功微  
 * [Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7167K->7161K(7168K)] 9215K->9209K(9728K), [Metaspace: 3529K->3529K(1056768K)], 0.0291829 secs] [Times: user=0.08 sys=0.02, real=0.03 secs]  
 */
 public class GCOverheadErrorDemo {  
  
    public static void main(String[] args) {  
        int i = 0;  
        List<String> list = new ArrayList<>();  
        try {  
            while (true) {  
            list.add(String.valueOf(++i).intern());  
            }  
        } catch (Throwable e) {  
            System.out.println("*****************i:" + i);  
            e.printStackTrace();  
            throw e;  
        }  
    }  
}

四、內存溢出之直接內存溢出錯誤

package com.jayway.oom;  
  
import java.nio.ByteBuffer;  
  
/**  
 * 直接內存溢出錯誤  
 * 拋出異常:Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory  
 * * 配置虛擬機參數:-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m  
 * * 致使緣由:一般NIO程序常用ByteBuffer來讀取或者寫入數據,這是一種基於通道(Channel)與緩衝區(Buffer)的IO方式,  
 * 它可使用Native函數庫直接分配堆外內存,而後經過一個存儲在java堆裏面的DirectByteBuffer對象做爲這塊內存的引用,  
 * 這樣能子一些場景中顯著提升性能,由於避免了在Java堆和Native內存中來回複製數據。  
 *  
 * ByteBuffer.allocate(capability):分配JVM堆內存,數據GC的管轄範圍,因爲須要拷貝因此速度相對較慢  
 *  
 * ByteBuffer.allocate(capability):分配OS本地內存,不屬於GC管轄範圍,因爲不須要內存拷貝,因此速度相對較快。  
 *  
 * 可是若是不斷分配本地內存,堆內存不多使用,那麼JVM就不須要執行GC,DirectByteBuffer對象就不會被回收,此時若是繼續分配堆外內存,  
 * 可能堆外內存已經被耗光了沒法繼續分配,此時程序就會拋出OutOfMemoryError,直接崩潰。  
 *  
 */
 public class DirectBufferMemoryErrorDemo {  
  
    public static void main(String[] args) {  
        //默認JVM配置的最大直接內存是總物理內存的四分之一  
        long maxDirectMemory = sun.misc.VM.maxDirectMemory() / 1024 / 1024;  
        System.out.println("配置的maxDirectMemory:" + maxDirectMemory + "MB");  
  
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 * 1024);  
    }  
  
}

五、內存溢出之沒法建立新的本地線程

package com.jayway.oom;  
  
/**  
 * 內存溢出之沒法建立新的本地線程  
 * 拋出異常:java.lang.OutOfMemoryError: unable to create new native thread  
 * * 描述:  
 * 高併發請求服務器時,常常出現java.lang.OutOfMemoryError: unable to create new native thread  
 *      native thread異常與對應的平臺有關  
 *  
 * 致使緣由:  
 *      一、應用程序建立了太多線程了,一個應用進程建立的線程數超過系統承載極限。  
 *      二、操做系統並不容許你的應用進程建立這麼多的線程,linux系統默認容許單個進程能夠建立的線程數是1024個  
 *  
 * 解決方法:  
 *      一、想辦法下降應用進程建立的線程數量,  
 *      二、若是應用程序確實須要這麼多線程,超過了linux系統的默認1024個限制,能夠經過修改linux服務器配置,提升這個閾值。  
 *  
 */
 public class UnableCreateNativeThreadErrorDemo {  
  
    public static void main(String[] args) {  
        for (int i = 0; true; i++) {  
            System.out.println("***************i:" + i);  
  
            //不斷得建立新線程,直到超過操做系統容許應用進程建立線程的極限  
            new Thread(() -> {  
                try {  
                    Thread.sleep(Integer.MAX_VALUE);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }).start();  
        }  
    }  
}

六、內存溢出之元空間溢出錯誤

package com.jayway.oom;  
  
import org.springframework.cglib.proxy.Enhancer;  
import org.springframework.cglib.proxy.MethodInterceptor;  
import org.springframework.cglib.proxy.MethodProxy;  
  
import java.lang.reflect.Method;  
  
/**  
 * 元空間溢出錯誤  
 * 拋出異常:java.lang.OutOfMemoryError: Metaspace  
 * * 設置虛擬機參數:-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=8m  
 * * 描述:Java8及之後的版本使用Metaspace來替代了永久代。metaspace是方法區在HotSpot中的實現,它與持久代最大的區別在於  
 *      Metaspace並不在虛擬機內存中而是在本地內存中。  
 *  
 * 元空間存儲瞭如下信息:  
 *      一、虛擬機加載的類信息  
 *      二、常量池  
 *      三、靜態變量  
 *      四、即時編譯後的代碼  
 *  
 */
 public class MetaspaceErrorDemo {  
  
    static class OOMTest {  
    }  
  
    public static void main(String[] args) {  
        int count = 0;  
  
        try {  
        //cglib不斷建立類,模擬Metaspace空間溢出,咱們不斷生成類往元空間中灌,超過元空間大小後就會拋出元空間移除的錯誤  
            while (true) {  
                count++;  
                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.invokeSuper(o, args);  
                    }  
                });  
                enhancer.create();  
            }  
        } catch (Throwable e) {  
            System.out.println("************多少次後發生了異常:" + count);  
            e.printStackTrace();  
        }  
    }  
}
相關文章
相關標籤/搜索