1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        常見OOM異常分析

        共 6724字,需瀏覽 14分鐘

         ·

        2021-04-30 14:16

        走過路過不要錯過

        點擊藍字關(guān)注我們


        前言

        放假這幾天,溫習(xí)了深入理解Java虛擬機的第二章, 整理了JVM發(fā)生OOM異常的幾種情況,并分析原因以及解決方案,希望對大家有幫助。

        Java 堆溢出

        Java堆用于存儲對象實例,只要不斷地創(chuàng)建對象,并且保證GC Roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象,那么在對象數(shù)量到達最大堆的容量限制后就會產(chǎn)生內(nèi)存溢出異常。

        Java 堆溢出原因

        • 無法在 Java 堆中分配對象

        • 應(yīng)用程序保存了無法被GC回收的對象。

        • 應(yīng)用程序過度使用 finalizer。

        Java 堆溢出排查解決思路

        1. 查找關(guān)鍵報錯信息,如


        java.lang.OutOfMemoryError: Java heap space
        1. 使用內(nèi)存映像分析工具(如Eclipsc Memory Analyzer或者Jprofiler)對Dump出來的堆儲存快照進行分析,分析清楚是內(nèi)存泄漏還是內(nèi)存溢出。

        2. 如果是內(nèi)存泄漏,可進一步通過工具查看泄漏對象到GC Roots的引用鏈,修復(fù)應(yīng)用程序中的內(nèi)存泄漏。

        3. 如果不存在泄漏,先檢查代碼是否有死循環(huán),遞歸等,再考慮用 -Xmx 增加堆大小。

        demo代碼

        package oom;
        import java.util.ArrayList;import java.util.List;
        /** * JVM配置參數(shù) * -Xms20m JVM初始分配的內(nèi)存20m * -Xmx20m JVM最大可用內(nèi)存為20m * -XX:+HeapDumpOnOutOfMemoryError 當(dāng)JVM發(fā)生OOM時,自動生成DUMP文件 * -XX:HeapDumpPath=/Users/weihuaxiao/Desktop/dump/ 生成DUMP文件的路徑 */public class HeapOOM { static class OOMObject { } public static void main(String[] args) { List<OOMObject> list = new ArrayList<OOMObject>(); //在堆中無限創(chuàng)建對象 while (true) { list.add(new OOMObject()); } }}

        運行結(jié)果

        按照前面的排查解決方案,我們來一波分析。

        1.查找報錯關(guān)鍵信息

        Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

        2. 使用內(nèi)存映像分析工具Jprofiler分析產(chǎn)生的堆儲存快照




        由圖可得,OOMObject這個類創(chuàng)建了810326個實例,是屬于內(nèi)存溢出,這時候先定位到對應(yīng)代碼,發(fā)現(xiàn)死循環(huán)導(dǎo)致的,修復(fù)即可。

        棧溢出

        關(guān)于虛擬機棧和本地方法棧,在Java虛擬機規(guī)范中描述了兩種異常:

        • 如果線程請求的棧深度大于虛擬機所允許的深度,將拋出StackOverflowError 異常;

        • 如果虛擬機??梢詣討B(tài)擴展,當(dāng)擴展時無法申請到足夠的內(nèi)存時會拋出 OutOfMemoryError 異常。

        棧溢出原因

        • 在單個線程下,棧幀太大,或者虛擬機棧容量太小,當(dāng)內(nèi)存無法分配的時候,虛擬機拋出StackOverflowError 異常。

        • 不斷地建立線程的方式會導(dǎo)致內(nèi)存溢出。

        棧溢出排查解決思路

        1. 查找關(guān)鍵報錯信息,確定是StackOverflowError還是OutOfMemoryError

        2. 如果是StackOverflowError,檢查代碼是否遞歸調(diào)用方法等

        3. 如果是OutOfMemoryError,檢查是否有死循環(huán)創(chuàng)建線程等,通過-Xss降低的每個線程棧大小的容量

        demo代碼

        package oom;
        /** * -Xss2M */public class JavaVMStackOOM { private void dontStop(){ while(true){
        } } public void stackLeakByThread(){ while(true){ Thread thread = new Thread(new Runnable(){ public void run() { dontStop(); } }); thread.start();} } public static void main(String[] args) { JavaVMStackOOM oom = new JavaVMStackOOM(); oom.stackLeakByThread(); }}

        運行結(jié)果

        1.查找報錯關(guān)鍵信息

        Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread

        2.確定是創(chuàng)建線程導(dǎo)致的棧溢出OOM

          Thread thread = new Thread(new Runnable(){                public void run() {                    dontStop();                }            });

        3.排查代碼,確定是否顯示使用死循環(huán)創(chuàng)建線程,或者隱式調(diào)用第三方接口創(chuàng)建線程(之前公司,調(diào)用騰訊云第三方接口,上傳圖片,遇到這個問題)

        方法區(qū)溢出

        方法區(qū),(又叫永久代,JDK8后,元空間替換了永久代),用于存放Class的相關(guān)信息,如類名、訪問修飾符、常量池、字段描述、方法描述等。運行時產(chǎn)生大量的類,會填滿方法區(qū),造成溢出。

        方法區(qū)溢出原因

        • 使用CGLib生成了大量的代理類,導(dǎo)致方法區(qū)被撐爆

        • 在Java7之前,頻繁的錯誤使用String.intern方法

        • 大量jsp和動態(tài)產(chǎn)生jsp

        • 應(yīng)用長時間運行,沒有重啟

        方法區(qū)溢出排查解決思路

        • 檢查是否永久代空間設(shè)置得過小

        • 檢查代碼是否頻繁錯誤得使用String.intern方法

        • 檢查是否跟jsp有關(guān)。

        • 檢查是否使用CGLib生成了大量的代理類

        • 重啟大法,重啟JVM

        demo代碼

        package oom;
        import org.springframework.cglib.proxy.Enhancer;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;
        import java.lang.reflect.Method;
        /** * jdk8以上的話, * 虛擬機參數(shù):-XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M */public class JavaMethodAreaOOM { 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(); } } static class OOMObject { }}

        運行結(jié)果

        1.查找報錯關(guān)鍵信息

        Caused by: java.lang.OutOfMemoryError: Metaspace

        2.檢查JVM元空間設(shè)置參數(shù)是否過小

        -XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M 

        3. 檢查對應(yīng)代碼,是否使用CGLib生成了大量的代理類

          while (true) {  ...   enhancer.setCallback(new MethodInterceptor() {                public Object intercept(Object obj, Method method,                                        Object[] args, MethodProxy proxy) throws Throwable {                    return proxy.invokeSuper(obj, args);                }            });            enhancer.create();  }

        本機直接內(nèi)存溢出

        直接內(nèi)存并不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,也不是Java 虛擬機規(guī)范中定義的內(nèi)存區(qū)域。但是,這部分內(nèi)存也被頻繁地使用,而且也可能導(dǎo)致OOM。

        在JDK1.4 中新加入了NIO(New Input/Output)類,它可以使用 native 函數(shù)庫直接分配堆外內(nèi)存,然后通過一個存儲在Java堆中的 DirectByteBuffer 對象作為這塊內(nèi)存的引用進行操作。這樣能在一些場景中顯著提高性能,因為避免了在 Java 堆和 Native 堆中來回復(fù)制數(shù)據(jù)。

        直接內(nèi)存溢出原因

        • 本機直接內(nèi)存的分配雖然不會受到Java 堆大小的限制,但是受到本機總內(nèi)存大小限制。

        • 直接內(nèi)存由 -XX:MaxDirectMemorySize 指定,如果不指定,則默認(rèn)與Java堆最大值(-Xmx指定)一樣。

        • NIO程序中,使用ByteBuffer.allocteDirect(capability)分配的是直接內(nèi)存,可能導(dǎo)致直接內(nèi)存溢出。

        直接內(nèi)存溢出

        • 檢查代碼是否恰當(dāng)

        • 檢查JVM參數(shù)-Xmx,-XX:MaxDirectMemorySize 是否合理。

        demo代碼

        package oom;
        import java.nio.ByteBuffer;import java.util.concurrent.TimeUnit;
        /** * -Xmx256m -XX:MaxDirectMemorySize=100M */public class DirectByteBufferTest { public static void main(String[] args) throws InterruptedException{ //分配128MB直接內(nèi)存 ByteBuffer bb = ByteBuffer.allocateDirect(1024*1024*128);
        TimeUnit.SECONDS.sleep(10); System.out.println("ok"); }}

        運行結(jié)果

        ByteBuffer分配128MB直接內(nèi)存,而JVM參數(shù)-XX:MaxDirectMemorySize=100M指定最大是100M,因此發(fā)生直接內(nèi)存溢出。

         ByteBuffer bb = ByteBuffer.allocateDirect(1024*1024*128);

        GC overhead limit exceeded

        • 這個是JDK6新加的錯誤類型,一般都是堆太小導(dǎo)致的。

        • Sun 官方對此的定義:超過98%的時間用來做GC并且回收了不到2%的堆內(nèi)存時會拋出此異常。

        解決方案

        • 檢查項目中是否有大量的死循環(huán)或有使用大內(nèi)存的代碼,優(yōu)化代碼。

        • 檢查JVM參數(shù)-Xmx -Xms是否合理

        • dump內(nèi)存,檢查是否存在內(nèi)存泄露,如果沒有,加大內(nèi)存。

        demo代碼

        package oom;
        import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;
        /** * JVm參數(shù) -Xmx8m -Xms8m */public class GCoverheadTest { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(10); for (int i = 0; i < Integer.MAX_VALUE; i++) { executor.execute(() -> { try { Thread.sleep(10000); } catch (InterruptedException e) { //do nothing } }); } }}

        運行結(jié)果

        實例代碼使用了newFixedThreadPool線程池,它使用了無界隊列,無限循環(huán)執(zhí)行任務(wù),會導(dǎo)致內(nèi)存飆升。因為設(shè)置了堆比較小,所以出現(xiàn)此類型OOM。

        總結(jié)

        本文介紹了以下幾種常見OOM異常

        java.lang.OutOfMemoryError: Java heap spacejava.lang.OutOfMemoryError: unable to create new native threadjava.lang.OutOfMemoryError: Metaspacejava.lang.OutOfMemoryError: Direct buffer memoryjava.lang.OutOfMemoryError: GC overhead limit exceeded

        希望大家遇到OOM異常時,對癥下藥,順利解決問題。同時,如果有哪里寫得不對,歡迎指出,感激不盡。





        往期精彩推薦



        騰訊、阿里、滴滴后臺面試題匯總總結(jié) — (含答案)

        面試:史上最全多線程面試題 !

        最新阿里內(nèi)推Java后端面試題

        JVM難學(xué)?那是因為你沒認(rèn)真看完這篇文章


        END


        關(guān)注作者微信公眾號 —《JAVA爛豬皮》


        了解更多java后端架構(gòu)知識以及最新面試寶典


        你點的每個好看,我都認(rèn)真當(dāng)成了


        看完本文記得給作者點贊+在看哦~~~大家的支持,是作者源源不斷出文的動力

        瀏覽 67
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            色伊人| 水蜜桃视频在线观看 | 欧美在线性爱 | 少妇床戏愉情 | 趴着把屁股撅起来c | 国产秘 精品入口 豆花 | 久久黄色免费网站 | 揉我胸啊嗯~出水l | 天天艹天天日 | 一起操逼 |