一般你去面試的時候,面試官經常會問:請談談你對OOM的認識?然後,你可能會說OOM就是out of memory,那如果你隻是這麼答的話,這可不是面試官想要的答案;面試官又接着問,那你生産過程中有遇到哪些OOM呢?請你說說出常見的OOM問題?這時的你可能是懵的。你知道幾種常見的OOM呢?歡迎評論區留言。
常見的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();
}
}
}
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源碼進階」,擷取海量java,大資料,機器學習資料!