麻豆成人91精品二区三区,国产91在线播放,加勒比无码专区中文字幕,欧美黑人XXXX高潮猛交

×

教你寫 Bug,常見的 OOM 異常分析

分類:互聯(lián)網(wǎng)熱點(diǎn) 編輯:新網(wǎng)小青年 瀏覽量:284
2020-07-20 13:33:06
在《Java虛擬機(jī)規(guī)范》的規(guī)定里,除了程序計(jì)數(shù)器外,虛擬機(jī)內(nèi)存的其他幾個(gè)運(yùn)行時(shí)區(qū)域都有發(fā)生 OutOfMemoryError 異常的可能。本篇主要包括如下 OOM 的介紹和示例:java.lang.StackOverflowErrorjava.lang.OutOfMemoryError: Java heap spacejava.lang.OutOfMemoryError: GC overhead limit exceededjava.lang.OutOfMemoryError-->Metaspacejava.lang.OutOfMemoryError: Direct buffer memoryjava.lang.OutOfMemoryError: unable to create new native threadjava.lang.OutOfMemoryError:Metaspacejava.lang.OutOfMemoryError: Requested array size exceeds VM limitjava.lang.OutOfMemoryError: Out of swap spacejava.lang.OutOfMemoryError:Kill process or sacrifice child我們常說的 OOM 異常,其實(shí)是 Error一. StackOverflowError1.1 寫個(gè) bugpublic class StackOverflowErrorDemo { public static void main(String[] args) { javaKeeper(); } private static void javaKeeper() { javaKeeper(); }}上一篇詳細(xì)的介紹過JVM 運(yùn)行時(shí)數(shù)據(jù)區(qū),JVM 虛擬機(jī)棧是有深度的,在執(zhí)行方法的時(shí)候會(huì)伴隨著入棧和出棧,上邊的方法可以看到,main 方法執(zhí)行后不停的遞歸,遲早把棧撐爆了Exception in thread "main" java.lang.StackOverflowError at oom.StackOverflowErrorDemo.javaKeeper(StackOverflowErrorDemo.java:15)1.2 原因分析無限遞歸循環(huán)調(diào)用(最常見原因),要時(shí)刻注意代碼中是否有了循環(huán)調(diào)用方法而無法退出的情況執(zhí)行了大量方法,導(dǎo)致線程棧空間耗盡方法內(nèi)聲明了海量的局部變量native 代碼有棧上分配的邏輯,并且要求的內(nèi)存還不小,比如 java.net.SocketInputStream.read0 會(huì)在棧上要求分配一個(gè) 64KB 的緩存(64位 Linux)1.3 解決方案修復(fù)引發(fā)無限遞歸調(diào)用的異常代碼, 通過程序拋出的異常堆棧,找出不斷重復(fù)的代碼行,按圖索驥,修復(fù)無限遞歸 Bug排查是否存在類之間的循環(huán)依賴(當(dāng)兩個(gè)對(duì)象相互引用,在調(diào)用toString方法時(shí)也會(huì)產(chǎn)生這個(gè)異常)通過 JVM 啟動(dòng)參數(shù) -Xss 增加線程棧內(nèi)存空間, 某些正常使用場(chǎng)景需要執(zhí)行大量方法或包含大量局部變量,這時(shí)可以適當(dāng)?shù)靥岣呔€程??臻g限制二. Java heap spaceJava 堆用于存儲(chǔ)對(duì)象實(shí)例,我們只要不斷的創(chuàng)建對(duì)象,并且保證 GC Roots 到對(duì)象之間有可達(dá)路徑來避免 GC 清除這些對(duì)象,那隨著對(duì)象數(shù)量的增加,總?cè)萘坑|及堆的最大容量限制后就會(huì)產(chǎn)生內(nèi)存溢出異常。Java 堆內(nèi)存的 OOM 異常是實(shí)際應(yīng)用中最常見的內(nèi)存溢出異常。2.1 寫個(gè) bug/** * JVM參數(shù):-Xmx12m */public class JavaHeapSpaceDemo { static final int SIZE = 2 * 1024 * 1024; public static void main(String[] a) { int[] i = new int; }}代碼試圖分配容量為 2M 的 int 數(shù)組,如果指定啟動(dòng)參數(shù) -Xmx12m,分配內(nèi)存就不夠用,就類似于將 XXXL 號(hào)的對(duì)象,往 S 號(hào)的 Java heap space 里面塞。Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at oom.JavaHeapSpaceDemo.main(JavaHeapSpaceDemo.java:13)2.2 原因分析請(qǐng)求創(chuàng)建一個(gè)超大對(duì)象,通常是一個(gè)大數(shù)組超出預(yù)期的訪問量/數(shù)據(jù)量,通常是上游系統(tǒng)請(qǐng)求流量飆升,常見于各類促銷/秒殺活動(dòng),可以結(jié)合業(yè)務(wù)流量指標(biāo)排查是否有尖狀峰值過度使用終結(jié)器(Finalizer),該對(duì)象沒有立即被 GC內(nèi)存泄漏(Memory Leak),大量對(duì)象引用沒有釋放,JVM 無法對(duì)其自動(dòng)回收,常見于使用了 File 等資源沒有回收2.3 解決方案針對(duì)大部分情況,通常只需要通過 -Xmx 參數(shù)調(diào)高 JVM 堆內(nèi)存空間即可。如果仍然沒有解決,可以參考以下情況做進(jìn)一步處理:如果是超大對(duì)象,可以檢查其合理性,比如是否一次性查詢了數(shù)據(jù)庫全部結(jié)果,而沒有做結(jié)果數(shù)限制如果是業(yè)務(wù)峰值壓力,可以考慮添加機(jī)器資源,或者做限流降級(jí)。如果是內(nèi)存泄漏,需要找到持有的對(duì)象,修改代碼設(shè)計(jì),比如關(guān)閉沒有釋放的連接面試官:說說內(nèi)存泄露和內(nèi)存溢出加送個(gè)知識(shí)點(diǎn),三連的終將成為大神~~內(nèi)存泄露和內(nèi)存溢出內(nèi)存溢出(out of memory),是指程序在申請(qǐng)內(nèi)存時(shí),沒有足夠的內(nèi)存空間供其使用,出現(xiàn)out of memory;比如申請(qǐng)了一個(gè) Integer,但給它存了 Long 才能存下的數(shù),那就是內(nèi)存溢出。內(nèi)存泄露( memory leak),是指程序在申請(qǐng)內(nèi)存后,無法釋放已申請(qǐng)的內(nèi)存空間,一次內(nèi)存泄露危害可以忽略,但內(nèi)存泄露堆積后果很嚴(yán)重,無論多少內(nèi)存,遲早會(huì)被占光。memory leak 最終會(huì)導(dǎo)致 out of memory!三、GC overhead limit exceededJVM 內(nèi)置了垃圾回收機(jī)制GC,所以作為 Javaer 的我們不需要手工編寫代碼來進(jìn)行內(nèi)存分配和釋放,但是當(dāng) Java 進(jìn)程花費(fèi) 98% 以上的時(shí)間執(zhí)行 GC,但只恢復(fù)了不到 2% 的內(nèi)存,且該動(dòng)作連續(xù)重復(fù)了 5 次,就會(huì)拋出 java.lang.OutOfMemoryError:GC overhead limit exceeded 錯(cuò)誤(俗稱:垃圾回收上頭)。簡單地說,就是應(yīng)用程序已經(jīng)基本耗盡了所有可用內(nèi)存, GC 也無法回收。假如不拋出 GC overhead limit exceeded 錯(cuò)誤,那 GC 清理的那么一丟丟內(nèi)存很快就會(huì)被再次填滿,迫使 GC 再次執(zhí)行,這樣惡性循環(huán),CPU 使用率 100%,而 GC 沒什么效果。3.1 寫個(gè) bug出現(xiàn)這個(gè)錯(cuò)誤的實(shí)例,其實(shí)我們寫個(gè)無限循環(huán),往 List 或 Map 加數(shù)據(jù)就會(huì)一直 Full GC,直到扛不住,這里用一個(gè)不容易發(fā)現(xiàn)的栗子。我們往 map 中添加 1000 個(gè)元素。/** * JVM 參數(shù): -Xmx14m -XX:+PrintGCDetails */public class KeylessEntry { static class Key { Integer id; Key(Integer id) { this.id = id; } @Override public int hashCode() { return id.hashCode(); } } public static void main(String[] args) { Map m = new HashMap(); while (true){ for (int i = 0; i < 1000; i++){ if (!m.containsKey(new Key(i))){ m.put(new Key(i), "Number:" + i); } } System.out.println("m.size()=" + m.size()); } }}...m.size()=54000m.size()=55000m.size()=56000Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded從輸出結(jié)果可以看到,我們的限制 1000 條數(shù)據(jù)沒有起作用,map 容量遠(yuǎn)超過了 1000,而且最后也出現(xiàn)了我們想要的錯(cuò)誤,這是因?yàn)轭?Key 只重寫了 hashCode() 方法,卻沒有重寫 equals() 方法,我們?cè)谑褂?containsKey() 方法其實(shí)就出現(xiàn)了問題,于是就會(huì)一直往 HashMap 中添加 Key,直至 GC 都清理不掉。 ? 面試官又來了:說一下HashMap原理以及為什么需要同時(shí)實(shí)現(xiàn)equals和hashcode執(zhí)行這個(gè)程序的最終錯(cuò)誤,和 JVM 配置也會(huì)有關(guān)系,如果設(shè)置的堆內(nèi)存特別小,會(huì)直接報(bào) Java heap space。算是被這個(gè)錯(cuò)誤截胡了,所以有時(shí),在資源受限的情況下,無法準(zhǔn)確預(yù)測(cè)程序會(huì)死于哪種具體的原因。3.2 解決方案添加 JVM 參數(shù)-XX:-UseGCOverheadLimit 不推薦這么干,沒有真正解決問題,只是將異常推遲檢查項(xiàng)目中是否有大量的死循環(huán)或有使用大內(nèi)存的代碼,優(yōu)化代碼dump內(nèi)存分析,檢查是否存在內(nèi)存泄露,如果沒有,加大內(nèi)存四、Direct buffer memory我們使用 NIO 的時(shí)候經(jīng)常需要使用 ByteBuffer 來讀取或?qū)懭霐?shù)據(jù),這是一種基于 Channel(通道) 和 Buffer(緩沖區(qū))的 I/O 方式,它可以使用 Native 函數(shù)庫直接分配堆外內(nèi)存,然后通過一個(gè)存儲(chǔ)在 Java 堆里面的 DirectByteBuffer 對(duì)象作為這塊內(nèi)存的引用進(jìn)行操作。這樣在一些場(chǎng)景就避免了 Java 堆和 Native 中來回復(fù)制數(shù)據(jù),所以性能會(huì)有所提高。Java 允許應(yīng)用程序通過 Direct ByteBuffer 直接訪問堆外內(nèi)存,許多高性能程序通過 Direct ByteBuffer 結(jié)合內(nèi)存映射文件(Memory Mapped File)實(shí)現(xiàn)高速 IO。4.1 寫個(gè) bugByteBuffer.allocate(capability) 是分配 JVM 堆內(nèi)存,屬于 GC 管轄范圍,需要內(nèi)存拷貝所以速度相對(duì)較慢;ByteBuffer.allocateDirect(capability) 是分配 OS 本地內(nèi)存,不屬于 GC 管轄范圍,由于不需要內(nèi)存拷貝所以速度相對(duì)較快;如果不斷分配本地內(nèi)存,堆內(nèi)存很少使用,那么 JVM 就不需要執(zhí)行 GC,DirectByteBuffer 對(duì)象就不會(huì)被回收,這時(shí)雖然堆內(nèi)存充足,但本地內(nèi)存可能已經(jīng)不夠用了,就會(huì)出現(xiàn) OOM,本地直接內(nèi)存溢出。/** * VM Options:-Xms10m,-Xmx10m,-XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m */public class DirectBufferMemoryDemo { public static void main(String[] args) { System.out.println("maxDirectMemory is:"+sun.misc.VM.maxDirectMemory() / 1024 / 1024 + "MB"); //ByteBuffer buffer = ByteBuffer.allocate(6*1024*1024); ByteBuffer buffer = ByteBuffer.allocateDirect(6*1024*1024); }}最大直接內(nèi)存,默認(rèn)是電腦內(nèi)存的 1/4,所以我們?cè)O(shè)小點(diǎn),然后使用直接內(nèi)存超過這個(gè)值,就會(huì)出現(xiàn) OOM。maxDirectMemory is:5MBException in thread "main" java.lang.OutOfMemoryError: Direct buffer memory4.2 解決方案Java 只能通過 ByteBuffer.allocateDirect 方法使用 Direct ByteBuffer,因此,可以通過 Arthas 等在線診斷工具攔截該方法進(jìn)行排查檢查是否直接或間接使用了 NIO,如 netty,jetty 等通過啟動(dòng)參數(shù) -XX:MaxDirectMemorySize 調(diào)整 Direct ByteBuffer 的上限值檢查 JVM 參數(shù)是否有 -XX:+DisableExplicitGC 選項(xiàng),如果有就去掉,因?yàn)樵搮?shù)會(huì)使 System.gc() 失效檢查堆外內(nèi)存使用代碼,確認(rèn)是否存在內(nèi)存泄漏;或者通過反射調(diào)用 sun.misc.Cleaner 的 clean() 方法來主動(dòng)釋放被 Direct ByteBuffer 持有的內(nèi)存空間內(nèi)存容量確實(shí)不足,升級(jí)配置五、Unable to create new native thread每個(gè) Java 線程都需要占用一定的內(nèi)存空間,當(dāng) JVM 向底層操作系統(tǒng)請(qǐng)求創(chuàng)建一個(gè)新的 native 線程時(shí),如果沒有足夠的資源分配就會(huì)報(bào)此類錯(cuò)誤。5.1 寫個(gè) bugpublic static void main(String[] args) { while(true){ new Thread(() -> { try { Thread.sleep(Integer.MAX_VALUE); } catch(InterruptedException e) { } }).start(); }}Error occurred during initialization of VMjava.lang.OutOfMemoryError: unable to create new native thread5.2 原因分析JVM 向 OS 請(qǐng)求創(chuàng)建 native 線程失敗,就會(huì)拋出 Unableto createnewnativethread,常見的原因包括以下幾類:線程數(shù)超過操作系統(tǒng)最大線程數(shù)限制(和平臺(tái)有關(guān))線程數(shù)超過 kernel.pid_max(只能重啟)native 內(nèi)存不足;該問題發(fā)生的常見過程主要包括以下幾步:JVM 內(nèi)部的應(yīng)用程序請(qǐng)求創(chuàng)建一個(gè)新的 Java 線程;JVM native 方法代理了該次請(qǐng)求,并向操作系統(tǒng)請(qǐng)求創(chuàng)建一個(gè) native 線程;操作系統(tǒng)嘗試創(chuàng)建一個(gè)新的 native 線程,并為其分配內(nèi)存;如果操作系統(tǒng)的虛擬內(nèi)存已耗盡,或是受到 32 位進(jìn)程的地址空間限制,操作系統(tǒng)就會(huì)拒絕本次 native 內(nèi)存分配;JVM 將拋出 java.lang.OutOfMemoryError:Unableto createnewnativethread 錯(cuò)誤。5.3 解決方案想辦法降低程序中創(chuàng)建線程的數(shù)量,分析應(yīng)用是否真的需要?jiǎng)?chuàng)建這么多線程如果確實(shí)需要?jiǎng)?chuàng)建很多線程,調(diào)高 OS 層面的線程最大數(shù):執(zhí)行 ulimia-a 查看最大線程數(shù)限制,使用 ulimit-u xxx 調(diào)整最大線程數(shù)限制六、MetaspaceJDK 1.8 之前會(huì)出現(xiàn) Permgen space,該錯(cuò)誤表示永久代(Permanent Generation)已用滿,通常是因?yàn)榧虞d的 class 數(shù)目太多或體積太大。隨著 1.8 中永久代的取消,就不會(huì)出現(xiàn)這種異常了。Metaspace 是方法區(qū)在 HotSpot 中的實(shí)現(xiàn),它與永久代最大的區(qū)別在于,元空間并不在虛擬機(jī)內(nèi)存中而是使用本地內(nèi)存,但是本地內(nèi)存也有打滿的時(shí)候,所以也會(huì)有異常。6.1 寫個(gè) bug/** * JVM Options: -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m */public class MetaspaceOOMDemo { public static void main(String[] args) { while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(MetaspaceOOMDemo.class); enhancer.setUseCache(false); enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> { //動(dòng)態(tài)代理創(chuàng)建對(duì)象 return methodProxy.invokeSuper(o, objects); }); enhancer.create(); } }}借助 Spring 的 GCLib 實(shí)現(xiàn)動(dòng)態(tài)創(chuàng)建對(duì)象Exception in thread "main" org.springframework.cglib.core.CodeGenerationException: java.lang.OutOfMemoryError-->Metaspace6.2 解決方案方法區(qū)溢出也是一種常見的內(nèi)存溢出異常,在經(jīng)常運(yùn)行時(shí)生成大量動(dòng)態(tài)類的應(yīng)用場(chǎng)景中,就應(yīng)該特別關(guān)注這些類的回收情況。這類場(chǎng)景除了上邊的 GCLib 字節(jié)碼增強(qiáng)和動(dòng)態(tài)語言外,常見的還有,大量 JSP 或動(dòng)態(tài)產(chǎn)生 JSP 文件的應(yīng)用(遠(yuǎn)古時(shí)代的傳統(tǒng)軟件行業(yè)可能會(huì)有)、基于 OSGi 的應(yīng)用(即使同一個(gè)類文件,被不同的加載器加載也會(huì)視為不同的類)等。方法區(qū)在 JDK8 中一般不太容易產(chǎn)生,HotSpot 提供了一些參數(shù)來設(shè)置元空間,可以起到預(yù)防作用-XX:MaxMetaspaceSize 設(shè)置元空間最大值,默認(rèn)是 -1,表示不限制(還是要受本地內(nèi)存大小限制的)-XX:MetaspaceSize 指定元空間的初始空間大小,以字節(jié)為單位,達(dá)到該值就會(huì)觸發(fā) GC 進(jìn)行類型卸載,同時(shí)收集器會(huì)對(duì)該值進(jìn)行調(diào)整-XX:MinMetaspaceFreeRatio 在 GC 之后控制最小的元空間剩余容量的百分比,可減少因元空間不足導(dǎo)致的垃圾收集頻率,類似的還有 MaxMetaspaceFreeRatio七、Requested array size exceeds VM limit7.1 寫個(gè) bugpublic static void main(String[] args) { int[] arr = new int;}這個(gè)比較簡單,建個(gè)超級(jí)大數(shù)組就會(huì)出現(xiàn) OOM,不多說了Exception in thread "main" java.lang.OutOfMemoryError: Requested array size exceeds VM limitJVM 限制了數(shù)組的最大長度,該錯(cuò)誤表示程序請(qǐng)求創(chuàng)建的數(shù)組超過最大長度限制。JVM 在為數(shù)組分配內(nèi)存前,會(huì)檢查要分配的數(shù)據(jù)結(jié)構(gòu)在系統(tǒng)中是否可尋址,通常為 Integer.MAX_VALUE-2。此類問題比較罕見,通常需要檢查代碼,確認(rèn)業(yè)務(wù)是否需要?jiǎng)?chuàng)建如此大的數(shù)組,是否可以拆分為多個(gè)塊,分批執(zhí)行。八、Out of swap space啟動(dòng) Java 應(yīng)用程序會(huì)分配有限的內(nèi)存。此限制是通過-Xmx和其他類似的啟動(dòng)參數(shù)指定的。在 JVM 請(qǐng)求的總內(nèi)存大于可用物理內(nèi)存的情況下,操作系統(tǒng)開始將內(nèi)容從內(nèi)存換出到硬盤驅(qū)動(dòng)器。該錯(cuò)誤表示所有可用的虛擬內(nèi)存已被耗盡。虛擬內(nèi)存(Virtual Memory)由物理內(nèi)存(Physical Memory)和交換空間(Swap Space)兩部分組成。這種錯(cuò)誤沒見過~~~九、Kill process or sacrifice child操作系統(tǒng)是建立在流程概念之上的。這些進(jìn)程由幾個(gè)內(nèi)核作業(yè)負(fù)責(zé),其中一個(gè)名為“ Out of memory Killer”,它會(huì)在可用內(nèi)存極低的情況下“殺死”(kill)某些進(jìn)程。OOM Killer 會(huì)對(duì)所有進(jìn)程進(jìn)行打分,然后將評(píng)分較低的進(jìn)程“殺死”,具體的評(píng)分規(guī)則可以參考 Surviving the Linux OOM Killer。不同于其他的 OOM 錯(cuò)誤, Killprocessorsacrifice child 錯(cuò)誤不是由 JVM 層面觸發(fā)的,而是由操作系統(tǒng)層面觸發(fā)的。9.1 原因分析默認(rèn)情況下,Linux 內(nèi)核允許進(jìn)程申請(qǐng)的內(nèi)存總量大于系統(tǒng)可用內(nèi)存,通過這種“錯(cuò)峰復(fù)用”的方式可以更有效的利用系統(tǒng)資源。然而,這種方式也會(huì)無可避免地帶來一定的“超賣”風(fēng)險(xiǎn)。例如某些進(jìn)程持續(xù)占用系統(tǒng)內(nèi)存,然后導(dǎo)致其他進(jìn)程沒有可用內(nèi)存。此時(shí),系統(tǒng)將自動(dòng)激活 OOM Killer,尋找評(píng)分低的進(jìn)程,并將其“殺死”,釋放內(nèi)存資源。9.2 解決方案升級(jí)服務(wù)器配置/隔離部署,避免爭用OOM Killer 調(diào)優(yōu)。最后附上一張“涯?!贝笊竦膱D參考與感謝《深入理解 Java 虛擬機(jī) 第 3 版》https://plumbr.io/outofmemoryerrorhttps://yq.aliyun.com/articles/711191https://github.com/StabilityMan/StabilityGuide/blob/master/docs/diagnosis/jvm/exception完 ●JVM內(nèi)存模型●JVM GC算法●不可不知的 7 個(gè) JDK 命令覺得不錯(cuò),點(diǎn)個(gè)在看~

聲明:免責(zé)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻(xiàn)自行上傳,本網(wǎng)站不擁有所有權(quán),也不承認(rèn)相關(guān)法律責(zé)任。如果您發(fā)現(xiàn)本社區(qū)中有涉嫌抄襲的內(nèi)容,請(qǐng)發(fā)

送郵件至:operations@xinnet.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),本站將立刻刪除涉嫌侵權(quán)內(nèi)容。本站原創(chuàng)內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)

需注明出處:新網(wǎng)idc知識(shí)百科

免費(fèi)咨詢獲取折扣

Loading
WWW夜夜夜骑骑骑| 尹人综| 国农村精品国产自线拍| 久久一精品视频| AA久久精选| 狼友看片网| av中文字幕第一页| 日本va欧美va欧美va精品| 久久97性感人妻| 无码高潮潮喷视频| 欧美A级超大胆视频| 欧美日韩精品乱国产| 久久a视频| 日韩东京热av| 天天干夜夜拍流| 杨幂一区二区三区| 亚洲中文字幕无码中文字在线| 日韩狠狠色| 中文人妻系列无码| 黄色亚洲性生活| 中文字幕一区二区视频| 91avsese| 美丽美女被操国产| 日本天套视频| 久久国产免费| 欧美在线观看不卡| AV无码国产亚洲| 五月丁香激情五| 井陉县| 亚洲成av人片在www色猫咪| 亚洲色图在线播放| 97蜜臀国产成人| 日韩精品免费大片网站| 大家论坛| 亚洲AV成人无码网站…| 亚韩欧美久久一区| 人妻中字avhd| 欧美国产精品区| 2021国产精品| 国产 中文 制服丝袜 另类| avav国产|