idea内存溢出解决_各种OOM代码样例及解决方法
針對目前大家對OOM的類型不太熟悉,那么來總結一下各種OOM出現的情況以及解決方法。把各種OOM的情況列出來,然后逐一進行代碼編寫復現和提供解決方法。
1. 堆溢出-java.lang.OutOfMemoryError: Java heap space。
2. 棧溢出-java.lang.OutOfMemorryError。
3. 棧溢出-java.lang.StackOverFlowError。
4. 元信息溢出-java.lang.OutOfMemoryError: Metaspace。
5. 直接內存溢出-java.lang.OutOfMemoryError: Direct buffer memory。
6. GC超限-java.lang.OutOfMemoryError: GC overhead limit exceeded。
0x01: 堆溢出異常,相信大家很常見。即堆內對象不能進行回收了,堆內存持續增大,這樣達到了堆內存的最大值,數據滿了,所以就出來了。我們直接放溢出的代碼樣例。需要設置好idea的VM Options: -Xmx100m,這樣設置為最大堆內存,這樣運行起來就很快就出來錯誤了。
package?oom;import?java.util.ArrayList;import?java.util.List;import?java.util.concurrent.TimeUnit;public?class?HeapOOM?{????static?class?OOMObject?{????}????public?static?void?main(String[]?args)?throws?InterruptedException?{????????List?list?=?new?ArrayList<>();????????while(true)?{//????????????TimeUnit.MILLISECONDS.sleep(1);????????????list.add(new?OOMObject());????????}????}}運行的異常如下,代碼直接就出來我們看到的異常了。
/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/bin/java?-Xmx100mException?in?thread?"main"?java.lang.OutOfMemoryError:?Java?heap?space????at?java.util.Arrays.copyOf(Arrays.java:3210)????at?java.util.Arrays.copyOf(Arrays.java:3181)????at?java.util.ArrayList.grow(ArrayList.java:261)????at?java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)????at?java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)????at?java.util.ArrayList.add(ArrayList.java:458)????at?oom.HeapOOM.main(HeapOOM.java:21)Process?finished?with?exit?code?1細心的小伙伴可以發現代碼中設置了一個休眠,目的是看一下堆內存的結構和數據圖。將休眠代碼打開,然后打開JDK自帶的jconsole命令,連接上之后看一下概覽圖,通過下圖發現堆內存持續不斷的增長。
打開內存界面,看一下內存,然后點一下GC按鈕,這個時候會有一些類進行回收,但是還是會繼續增長,看一下下面的圖。
點開信息標簽看一下。經過幾次GC回收之后,類的數據量還是變化不大,說明沒有進行回收。
以上這種情況的解決方法就是找到問題點,分析哪個地方是否存儲了大量類沒有被回收的情況,通過JMAP命令將線上的堆內存導出來后進行分析。
0x02: 看一下棧溢出的情況,下面的代碼就是無限的創建線程,直到沒法再創建線程。
package?oom;import?java.util.concurrent.TimeUnit;/**?*?@Date?2020-07-18?*/public?class?StackOOM?{????public?static?void?infiniteRun()?{????????while(true)?{????????????Thread?thread?=?new?Thread(()?->?{????????????????while?(true)?{????????????????????try?{????????????????????????TimeUnit.HOURS.sleep(1);????????????????????}?catch?(InterruptedException?e)?{????????????????????????e.printStackTrace();????????????????????}????????????????}????????????});????????????thread.start();????????}????}????public?static?void?main(String[]?args)?{????????infiniteRun();????}}拋出來的異常如下,如果真的需要創建線程,我們需要調整幀棧的大小-Xss512k,默認幀棧大小為1M,如果設置小了,可以創建更多線程。
/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/bin/java?-Xss512k?Exception?in?thread?"main"?java.lang.OutOfMemoryError:?unable?to?create?new?native?thread????at?java.lang.Thread.start0(Native?Method)????at?java.lang.Thread.start(Thread.java:717)????at?oom.StackOOM.infiniteRun(StackOOM.java:24)????at?oom.StackOOM.main(StackOOM.java:29)Process?finished?with?exit?code?130?(interrupted?by?signal?2:?SIGINT)以上這種情況是幀棧不夠用了,如果出現了這種情況,需要了解什么地方創建了很多線程,線上程序需要用jstack命令,將當前線程的狀態導出來放到文件里邊,然后將文件上傳到fastthread.io網站上進行分析。
0x03:看一下棧溢出的另一種情況,這就是棧的StackOverFlow的情況。下面就是一個死循環遞歸調用。
package?oom;/**?*?@Date?2020-07-18?*/public?class?StackOFE?{????public?static?void?stackOverFlowErrorMethod()?{????????stackOverFlowErrorMethod();????}????public?static?void?main(String[]?args)?{????????stackOverFlowErrorMethod();????}}運行之后出現的錯誤如下,程序每次遞歸的時候,程序會把數據結果壓入棧,包括里邊的指針等,這個時候就需要幀棧大一些才能承受住更多的遞歸調用。通過-Xss進行設置,上邊的例子需要設置小一些,以分配更多的幀棧,這次是一個幀棧需要記錄程序數據,所以需要更大的值。
/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/bin/java?-Xss2mException?in?thread?"main"?java.lang.StackOverflowError????at?oom.StackOFE.stackOverFlowErrorMethod(StackOFE.java:10)????at?oom.StackOFE.stackOverFlowErrorMethod(StackOFE.java:10)????at?oom.StackOFE.stackOverFlowErrorMethod(StackOFE.java:10)????at?oom.StackOFE.stackOverFlowErrorMethod(StackOFE.java:10)????at?oom.StackOFE.stackOverFlowErrorMethod(StackOFE.java:10)????at?oom.StackOFE.stackOverFlowErrorMethod(StackOFE.java:10)遇到上面的情況下,那么就需要通過jstack將線程數據導到文件進行分析。找到遞歸的點,如果程序就是需要遞歸的次數的話,那么這個時候就需要增大幀棧的大小以適應程序。
0x04: 元數據區域溢出,元數據區域也成為方法區,存儲著類的相關信息,常量池,方法描述符,字段描述符,運行時產生大量的類就會造成這個區域的溢出。我們運行的時候指定一下元數據區域的大小,設置到idea的VM options里邊:-XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=30M。
package?oom;import?net.sf.cglib.proxy.Enhancer;import?net.sf.cglib.proxy.MethodInterceptor;import?net.sf.cglib.proxy.MethodProxy;import?java.lang.reflect.Method;/**?*?@Date?2020-07-18?*/public?class?MetaspaceOOM?{????static?class?OOMObject{}????public?static?void?main(String[]?args)?{????????while?(true)?{????????????Enhancer?enhancer?=?new?Enhancer();????????????enhancer.setSuperclass(OOMObject.class);????????????enhancer.setUseCache(false);????????????enhancer.setCallback(new?MethodInterceptor()?{????????????????public?Object?intercept(Object?obj,?Method?method,????????????????????????????????????????Object[]?args,?MethodProxy?proxy)?throws?Throwable?{????????????????????return?proxy.invokeSuper(obj,?args);????????????????}????????????});????????????enhancer.create();????????}????}}運行的結果如下,元數據信息溢出了。這種情況產生的原因有:通過CBLIG大量生成類,導致Meta信息滿了;JDK7的時候使用String.intern()不當,會產生大量常量數據;加載大量的jsp以及動態生成jsp文件。需要調整元數據空間的大小,如果調大了之后還出現了這種異常,我們需要分析哪里出現的溢出并fix掉。
/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/bin/java?-XX:MetaspaceSizeException?in?thread?"main"?java.lang.OutOfMemoryError:?Metaspace????at?net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:345)????at?net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)????at?net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:114)????at?net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291)????at?net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)????at?net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)????at?oom.MetaspaceOOM.main(MetaspaceOOM.java:28)Process?finished?with?exit?code?10x05: 直接內存溢出,除了使用堆內存外,還可能用直接內存,即堆外內存。NIO為了提高性能,避免在Java Heap和native Heap中切換,所以使用直接內存,默認情況下,直接內存的大小和對內存大小一致。堆外內存不受JVM的限制,但是受制于機器整體內存的大小限制。如下代碼設置堆最大內存為128m,直接內存為100m,然后我們每次分配1M放到list里邊。
-Xmx128m?-XX:MaxDirectMemorySize=100Mpackage?oom;import?java.nio.ByteBuffer;import?java.util.ArrayList;import?java.util.List;/**?*?@Date?2020-07-18?*/public?class?DirectBufferOOM?{????public?static?void?main(String[]?args)?{????????final?int?_1M?=?1024?*?1024?*?1;????????List?buffers?=?new?ArrayList<>();????????int?count?=?1;????????while?(true)?{????????????ByteBuffer?byteBuffer?=?ByteBuffer.allocateDirect(_1M);????????????buffers.add(byteBuffer);????????????System.out.println(count++);????????}????}}這個時候,當輸出100次的時候,下次再分配的時候會報OOM-Direct buffer memory。
/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/bin/java?-Xmx128m?Exception?in?thread?"main"?java.lang.OutOfMemoryError:?Direct?buffer?memory????at?java.nio.Bits.reserveMemory(Bits.java:694)????at?java.nio.DirectByteBuffer.(DirectByteBuffer.java:123)????at?java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)????at?oom.DirectBufferOOM.main(DirectBufferOOM.java:18)Process?finished?with?exit?code?1這種情況是我們使用直接內存造成溢出,這個時候我們需要檢查一下程序里邊是否使用的NIO及NIO,比如Netty,里邊的直接內存的配置。
0x06: JDK1.6之后新增了一個錯誤類型,如果堆內存太小的時候會報這個錯誤。如果98%的GC的時候回收不到2%的時候會報這個錯誤,也就是最小最大內存出現了問題的時候會報這個錯誤。如果代碼配置了最小最大堆內存都為10m。
-Xmx10m?-Xms10mpackage?oom;import?java.util.concurrent.ExecutorService;import?java.util.concurrent.Executors;/**?*?@Date?2020-07-18?*/public?class?GCOverheadOOM?{????public?static?void?main(String[]?args)?{????????ExecutorService?executor?=?Executors.newFixedThreadPool(5);????????for?(int?i?=?0;?i??{????????????????try?{????????????????????Thread.sleep(10000);????????????????}?catch?(InterruptedException?e)?{????????????????????//do?nothing????????????????}????????????});????????}????}}這個創建了一個線程池,如果線程池執行的時候如果核心線程處理不過來的時候會把數據放到LinkedBlockingQueue里邊,也就是堆內存當中。這個時候我們需要檢查-Xms -Xmx最小最大堆配置是否合理。再一個dump出現當前內存來分析一下是否使用了大量的循環或使用大量內存代碼。
以上就是經常遇到的情況,需要針對出現的不同情況進行分析和處理。
總結
以上是生活随笔為你收集整理的idea内存溢出解决_各种OOM代码样例及解决方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑怎么进入blos设置u盘启动 如何设
- 下一篇: alienware怎么进bios设置 a