Java|常見OutOfMemoryError場(chǎng)景,你遇到過(guò)哪些?

1、背景

java進(jìn)程執(zhí)行特定作業(yè)出現(xiàn)內(nèi)存溢出,跑很多類似作業(yè)是正常的。

2、常見問(wèn)題及原因分析

當(dāng)然這個(gè)問(wèn)題不難,主要是借助這個(gè)機(jī)會(huì)來(lái)一起整理下之前出現(xiàn)過(guò)的問(wèn)題,希望能舉一反三。

2.1 問(wèn)題場(chǎng)景一

Exception in thread thread_name: java.lang.OutOfMemoryError: Java heap space

一般會(huì)有三種情況導(dǎo)致:

  • 堆內(nèi)存太小,無(wú)法滿足應(yīng)用的需 要。

  • 內(nèi)存泄漏,泄漏的內(nèi)存被hold住,無(wú)法使用。

  • Finalizer線程清理實(shí)現(xiàn)了finalize()方法的對(duì)象速度慢于生成這些對(duì)象的速度。

    比如在main線程中,創(chuàng)建大量的實(shí)現(xiàn)了finalize()的類對(duì)象,由于main線程優(yōu)先級(jí)大于Finalizer線程,所以main有更多的cpu執(zhí)行時(shí)間,導(dǎo)致對(duì)象生成速度大于清除速度。

    class Finalizable {
    
         @Override
         protected void finalize() throws Throwable {
                try {
                        this.wait(0);
                     } catch(Exception e) {
                            System.err.println(e);
                      }
         }
    
          public static void main(String args[]) {
                //僅僅為了說(shuō)明問(wèn)題
                while(true) {
                   Finalizable f = new Finalizable();
              }
         }
    }
    

2.2 問(wèn)題場(chǎng)景二

Exception in thread thread_name: java.lang.OutOfMemoryError: GC Overhead limit exceeded

gc的內(nèi)存太小,大部分時(shí)間都在gc(98%),且回收的堆內(nèi)存不到2%,且持續(xù)fullgc到了第5次。存活對(duì)象無(wú)法裝入heap中,一般需要加大heap。

2.3 問(wèn)題場(chǎng)景三

Exception in thread thread_name: java.lang.OutOfMemoryError: Requested array size exceeds VM limit

數(shù)組對(duì)象太大, 無(wú)法載入堆內(nèi)存中,原因可能是heap太小或者程序不經(jīng)意產(chǎn)生了大數(shù)組對(duì)象,比較好排查。

2.4 問(wèn)題場(chǎng)景四

Exception in thread thread_name: java.lang.OutOfMemoryError: Metaspace

分配給java類元數(shù)據(jù)的本地內(nèi)存用盡通過(guò)增加MaxMetaSpaceSize,或者減少堆內(nèi)存解決。

2.5 問(wèn)題場(chǎng)景五

Exception in thread thread_name: java.lang.OutOfMemoryError: Compressed class space

簡(jiǎn)單理解就是說(shuō),在64位jvm出現(xiàn)之后,用于操作內(nèi)存的OOPS(原始指針)變成了64位。為了避免OOPS擴(kuò)大一倍帶來(lái)的性能損耗,使用UseCompressedOops參數(shù)可以用32位的指針來(lái)指向一個(gè)內(nèi)存空間。如果使用率參數(shù)UseCompressedClassPointers,那么也可以用32位的指針來(lái)指向操作的內(nèi)存區(qū)域,這些指針指向的區(qū)域被叫做“Compressed class space”。如果引入太多包,可能會(huì)超過(guò)Compressed class space導(dǎo)致出現(xiàn)該問(wèn)題,一般調(diào)大Compressed class space就好。

2.6 問(wèn)題場(chǎng)景六

Exception in thread thread_name: java.lang.OutOfMemoryError: reason stack_trace_with_native_method
本地方法內(nèi)存分配失敗,一般通過(guò)OS工具來(lái)定位問(wèn)題。

2.7 問(wèn)題場(chǎng)景七

Exception in thread thread_name: java.lang.OutOfMemoryError: Metaspace

Metadata用于保存jvm的靜態(tài)數(shù)據(jù)(如類和方法),分配給java類元數(shù)據(jù)的內(nèi)存用完,比如你的應(yīng)用加載很多類容易出現(xiàn)該問(wèn)題。通過(guò)增加MaxMetaSpaceSize,或者減少堆內(nèi)存解決。

總之,最后定位的問(wèn)題是第一種類型,也就是heap不夠,現(xiàn)在看下如何排查問(wèn)題。

3. 問(wèn)題定位

1、查看jvm的信息

  jinfo -flags {pid}

發(fā)現(xiàn)堆內(nèi)存的大小很小,只有128M。

2、導(dǎo)出進(jìn)程的堆內(nèi)存信息。
添加運(yùn)行的JVM參數(shù)(短駐進(jìn)程)

   XX:+HeapDumpOnOutOfMemeoryError
   XX:HeapDumpPath=/tmp/heap_error.gc

3、MAT分析堆內(nèi)存
發(fā)現(xiàn)ArrayList中占用的內(nèi)存超過(guò)70%,因此分析是存在批處理或者循環(huán)或者內(nèi)存泄漏。進(jìn)一步分析發(fā)現(xiàn)是批處理數(shù)據(jù)量太大(10000),并且每條數(shù)據(jù)量是文本數(shù)據(jù),大對(duì)象太多直接導(dǎo)致堆塞滿。

4、解決

  • 設(shè)置合理的批處理量
  • 設(shè)置更大的堆

參考

https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/memleaks002.html

https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/memleaks002.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容