天天看點

java.lang.OutOfMemoryError——java oom總結!

    一般你去面試的時候,面試官經常會問:請談談你對OOM的認識?然後,你可能會說OOM就是out of memory,那如果你隻是這麼答的話,這可不是面試官想要的答案;面試官又接着問,那你生産過程中有遇到哪些OOM呢?請你說說出常見的OOM問題?這時的你可能是懵的。你知道幾種常見的OOM呢?歡迎評論區留言。

  常見的OOM總述:

java.lang.OutOfMemoryError——java oom總結!

 1. StackOverflowError

    線程請求的棧深度大于虛拟機所允許的最大深度,将抛出StackOverflowError異常 。遞歸調用方法,如果沒有方法出口的方法會造成StackOverflowError,或者說如果調用的過深都會抛出,這種錯誤也比較容易定位。

public class StackOverflowErrorDemo {
    public static void main(String[] args){
        stackOverflowError();
    }

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

2. java.lang.OutOfMemoryError: Java heap space 

    溢出原因:深入了解Java虛拟機書中講到java堆溢出,Java堆用于存儲對象執行個體,隻要不斷地建立對象,并且保證GC Roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象,那麼在對象數量到達最大堆的容量限制後就會産生記憶體溢出異常。

import java.util.Random;

public class JavaHeapSpaceDemo {
    public static void main(String[] args){
        String str = "seu";

        while(true){
            str += str + new Random().nextInt(11111111)+new Random().nextInt(22222222);
            str.intern();
        }

    }
}
           
java.lang.OutOfMemoryError——java oom總結!

3. java.lang.OutOfMemoryError:GC overhead limit exceeded

import java.util.ArrayList;
import java.util.List;

/*
* GC回收時間長時會抛出OutOfMemoryError。過長的定義是,超過98%的時間用來做GC并且回收了
* 不到2%的堆記憶體,連續多次GC都隻回收了不到2%的極端情況下才會抛出。

假設不抛出GC overhead limit錯誤會發生什麼情況呢?
那就是GC清理的這麼點記憶體很快會再次填滿,迫使GC再次執行,這樣就形成惡性循環,CPU使用率一直
是100%,而GC缺沒有任何成果。
* */

public class GCOverheadDemo {
    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;
        }

    }
}
           

4. java.lang.OutOfMemoryError:Direct buffer memory

import java.nio.ByteBuffer;

/**
 * 配置參數: -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
 * 導緻原因:
 *  寫NIO程式經常使用ByteBuffer來讀取或者寫入資料,這是一種基于通道,緩沖區的IO方式
 *   ByteBuffer.alloction(capabicity)配置設定jvm堆記憶體,屬于GC管轄範圍
 *   ByteBuffer.allocateDirect(capabicity)配置設定本地記憶體,不屬于GC管轄範圍,由于不需要記憶體拷貝是以速度較快。
 *   如果不斷配置設定本地記憶體,但是堆記憶體很少使用,那麼jvm不需要執行GC,DirectByteBuffer對象就不會被回收,這時候堆記憶體充足
 *   但是本地記憶體已經使用光了,再次配置設定的時候就會出現OOM異常!
 *   --堆記憶體不夠的時候,會觸發fullGC順帶回收 DirectMemory!
 *   MaxDirectMemorySize 預設是64M
 */
public class DirectBufferMemoryDemo {
    public static void main(String[] args) {
        System.out.println("配置的MaxDirectMemory:"+(sun.misc.VM.maxDirectMemory()/(double)1024/1024)+"MB");
        try{Thread.sleep(3000);} catch (InterruptedException e) { e.printStackTrace();}

        //-XX:MaxDirectMemorySize=5m 我們配置為5M 但是實際使用是6M
        ByteBuffer.allocateDirect(6*1024*1024);
    }
}
           

5. java.lang.OutOfMemoryError:unable to create new native thread

     不知道你們生産環境是否會出現這種情況,高并發請求伺服器時,經常出現如下異常java.lang.OutOfMemoryError:unable to create new native thread。那出現的原因呢?

    1.建立太多的線程了

    2.伺服器的設定限制了你建立線程的數量了(linux 允許建立的線程數是1024個)

6. java.langOutOfMemoryError:Metaspace

    我們知道Java8及以後已經使用Metaspace來替代永久代,Metaspace并不在虛拟機記憶體中而是使用本地記憶體。主要還是是加載到記憶體中的 class 數量太多或者體積太大。這時可以通過JVM參數-XX:MaxMetaspaceSize指定。

* JVM參數: -XX:MetaspaceSize=8m  -XX:MaxMetaspaceSize=8m
 * Java8之後的版本使用Metaspace來替代永久代
 * Metaspace是方法區在HotSpot中的實作,它與持久帶最大的差別在于:Metaspace并不在虛拟機記憶體中而是使用
 * 本地記憶體,也即在java8中,class metaspace(the virtual machines internal presentation of java class)
 * ,被存儲在叫做Metaspace的native memory
 *
 * 永久代(Metaspace)存放以下資訊:
 * 虛拟機加載的類資訊
 * 常量池
 * 靜态變量
 * 即時編譯後的代碼
 * */

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class MetaspaceOOMT {
    static class OOMTest {

    }

    public static void main(String[] args) {
        int i = 0;//模拟多少次後發生異常

        try {
            while (true) {
                i++;
                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("********多少次後發生了異常:" + i);
            e.printStackTrace();
        }
    }
}
           

總結

 了解jvm才能更有深度的了解這些異常,對jvm的學習應該貫穿整個java!歡迎關注我的公衆号,一起學習!

java.lang.OutOfMemoryError——java oom總結!

                    關注「Java源碼進階」,擷取海量java,大資料,機器學習資料!