idea内存溢出解决_各种OOM代码样例及解决方法
針對(duì)目前大家對(duì)OOM的類型不太熟悉,那么來(lái)總結(jié)一下各種OOM出現(xiàn)的情況以及解決方法。把各種OOM的情況列出來(lái),然后逐一進(jìn)行代碼編寫復(fù)現(xiàn)和提供解決方法。
1. 堆溢出-java.lang.OutOfMemoryError: Java heap space。
2. 棧溢出-java.lang.OutOfMemorryError。
3. 棧溢出-java.lang.StackOverFlowError。
4. 元信息溢出-java.lang.OutOfMemoryError: Metaspace。
5. 直接內(nèi)存溢出-java.lang.OutOfMemoryError: Direct buffer memory。
6. GC超限-java.lang.OutOfMemoryError: GC overhead limit exceeded。
0x01: 堆溢出異常,相信大家很常見。即堆內(nèi)對(duì)象不能進(jìn)行回收了,堆內(nèi)存持續(xù)增大,這樣達(dá)到了堆內(nèi)存的最大值,數(shù)據(jù)滿了,所以就出來(lái)了。我們直接放溢出的代碼樣例。需要設(shè)置好idea的VM Options: -Xmx100m,這樣設(shè)置為最大堆內(nèi)存,這樣運(yùn)行起來(lái)就很快就出來(lái)錯(cuò)誤了。
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());????????}????}}運(yùn)行的異常如下,代碼直接就出來(lái)我們看到的異常了。
/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細(xì)心的小伙伴可以發(fā)現(xiàn)代碼中設(shè)置了一個(gè)休眠,目的是看一下堆內(nèi)存的結(jié)構(gòu)和數(shù)據(jù)圖。將休眠代碼打開,然后打開JDK自帶的jconsole命令,連接上之后看一下概覽圖,通過(guò)下圖發(fā)現(xiàn)堆內(nèi)存持續(xù)不斷的增長(zhǎng)。
打開內(nèi)存界面,看一下內(nèi)存,然后點(diǎn)一下GC按鈕,這個(gè)時(shí)候會(huì)有一些類進(jìn)行回收,但是還是會(huì)繼續(xù)增長(zhǎng),看一下下面的圖。
點(diǎn)開信息標(biāo)簽看一下。經(jīng)過(guò)幾次GC回收之后,類的數(shù)據(jù)量還是變化不大,說(shuō)明沒(méi)有進(jìn)行回收。
以上這種情況的解決方法就是找到問(wèn)題點(diǎn),分析哪個(gè)地方是否存儲(chǔ)了大量類沒(méi)有被回收的情況,通過(guò)JMAP命令將線上的堆內(nèi)存導(dǎo)出來(lái)后進(jìn)行分析。
0x02: 看一下棧溢出的情況,下面的代碼就是無(wú)限的創(chuàng)建線程,直到?jīng)]法再創(chuàng)建線程。
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();????}}拋出來(lái)的異常如下,如果真的需要?jiǎng)?chuàng)建線程,我們需要調(diào)整幀棧的大小-Xss512k,默認(rèn)幀棧大小為1M,如果設(shè)置小了,可以創(chuàng)建更多線程。
/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)以上這種情況是幀棧不夠用了,如果出現(xiàn)了這種情況,需要了解什么地方創(chuàng)建了很多線程,線上程序需要用jstack命令,將當(dāng)前線程的狀態(tài)導(dǎo)出來(lái)放到文件里邊,然后將文件上傳到fastthread.io網(wǎng)站上進(jìn)行分析。
0x03:看一下棧溢出的另一種情況,這就是棧的StackOverFlow的情況。下面就是一個(gè)死循環(huán)遞歸調(diào)用。
package?oom;/**?*?@Date?2020-07-18?*/public?class?StackOFE?{????public?static?void?stackOverFlowErrorMethod()?{????????stackOverFlowErrorMethod();????}????public?static?void?main(String[]?args)?{????????stackOverFlowErrorMethod();????}}運(yùn)行之后出現(xiàn)的錯(cuò)誤如下,程序每次遞歸的時(shí)候,程序會(huì)把數(shù)據(jù)結(jié)果壓入棧,包括里邊的指針等,這個(gè)時(shí)候就需要幀棧大一些才能承受住更多的遞歸調(diào)用。通過(guò)-Xss進(jìn)行設(shè)置,上邊的例子需要設(shè)置小一些,以分配更多的幀棧,這次是一個(gè)幀棧需要記錄程序數(shù)據(jù),所以需要更大的值。
/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)遇到上面的情況下,那么就需要通過(guò)jstack將線程數(shù)據(jù)導(dǎo)到文件進(jìn)行分析。找到遞歸的點(diǎn),如果程序就是需要遞歸的次數(shù)的話,那么這個(gè)時(shí)候就需要增大幀棧的大小以適應(yīng)程序。
0x04: 元數(shù)據(jù)區(qū)域溢出,元數(shù)據(jù)區(qū)域也成為方法區(qū),存儲(chǔ)著類的相關(guān)信息,常量池,方法描述符,字段描述符,運(yùn)行時(shí)產(chǎn)生大量的類就會(huì)造成這個(gè)區(qū)域的溢出。我們運(yùn)行的時(shí)候指定一下元數(shù)據(jù)區(qū)域的大小,設(shè)置到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();????????}????}}運(yùn)行的結(jié)果如下,元數(shù)據(jù)信息溢出了。這種情況產(chǎn)生的原因有:通過(guò)CBLIG大量生成類,導(dǎo)致Meta信息滿了;JDK7的時(shí)候使用String.intern()不當(dāng),會(huì)產(chǎn)生大量常量數(shù)據(jù);加載大量的jsp以及動(dòng)態(tài)生成jsp文件。需要調(diào)整元數(shù)據(jù)空間的大小,如果調(diào)大了之后還出現(xiàn)了這種異常,我們需要分析哪里出現(xiàn)的溢出并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: 直接內(nèi)存溢出,除了使用堆內(nèi)存外,還可能用直接內(nèi)存,即堆外內(nèi)存。NIO為了提高性能,避免在Java Heap和native Heap中切換,所以使用直接內(nèi)存,默認(rèn)情況下,直接內(nèi)存的大小和對(duì)內(nèi)存大小一致。堆外內(nèi)存不受JVM的限制,但是受制于機(jī)器整體內(nèi)存的大小限制。如下代碼設(shè)置堆最大內(nèi)存為128m,直接內(nèi)存為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++);????????}????}}這個(gè)時(shí)候,當(dāng)輸出100次的時(shí)候,下次再分配的時(shí)候會(huì)報(bào)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這種情況是我們使用直接內(nèi)存造成溢出,這個(gè)時(shí)候我們需要檢查一下程序里邊是否使用的NIO及NIO,比如Netty,里邊的直接內(nèi)存的配置。
0x06: JDK1.6之后新增了一個(gè)錯(cuò)誤類型,如果堆內(nèi)存太小的時(shí)候會(huì)報(bào)這個(gè)錯(cuò)誤。如果98%的GC的時(shí)候回收不到2%的時(shí)候會(huì)報(bào)這個(gè)錯(cuò)誤,也就是最小最大內(nèi)存出現(xiàn)了問(wèn)題的時(shí)候會(huì)報(bào)這個(gè)錯(cuò)誤。如果代碼配置了最小最大堆內(nèi)存都為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????????????????}????????????});????????}????}}這個(gè)創(chuàng)建了一個(gè)線程池,如果線程池執(zhí)行的時(shí)候如果核心線程處理不過(guò)來(lái)的時(shí)候會(huì)把數(shù)據(jù)放到LinkedBlockingQueue里邊,也就是堆內(nèi)存當(dāng)中。這個(gè)時(shí)候我們需要檢查-Xms -Xmx最小最大堆配置是否合理。再一個(gè)dump出現(xiàn)當(dāng)前內(nèi)存來(lái)分析一下是否使用了大量的循環(huán)或使用大量?jī)?nèi)存代碼。
以上就是經(jīng)常遇到的情況,需要針對(duì)出現(xiàn)的不同情況進(jìn)行分析和處理。
總結(jié)
以上是生活随笔為你收集整理的idea内存溢出解决_各种OOM代码样例及解决方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 电脑怎么进入blos设置u盘启动 如何设
- 下一篇: alienware怎么进bios设置 a