衡阳派盒市场营销有限公司

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

完蛋!我被 Out of Memory 包圍了!

京東云 ? 來(lái)源:jf_75140285 ? 作者:jf_75140285 ? 2024-08-15 14:23 ? 次閱讀

先點(diǎn)贊再看,養(yǎng)成好習(xí)慣

是極致魅惑、灑脫自由的 Java heap space

是知性柔情、溫婉大氣的 GC overhead limit exceeded

是純真無(wú)邪、活潑可愛(ài)的 Metaspace

如果以上不是你的菜,那還有……

刁蠻任性,無(wú)跡可尋的 CodeCache

性感火辣、心思細(xì)膩的 Direct Memory

高貴冷艷,獨(dú)愛(ài)你一人的 OOM Killer

總有一款,能讓你鐘情!BUG 選擇權(quán),現(xiàn)在交由你手!

Java heap space

這是最常見(jiàn)的一個(gè) OOM 問(wèn)題了,誰(shuí)還沒(méi)經(jīng)歷過(guò)一個(gè) Heap OOM呢?

當(dāng)堆內(nèi)存被塞滿之后,一邊 GC 無(wú)法及時(shí)回收,一邊又在繼續(xù)創(chuàng)建新對(duì)象,Allocator 無(wú)法分配新的內(nèi)存之后,就會(huì)送一個(gè) OOM 的錯(cuò)誤:

java.lang.OutOfMemoryError: Java heap space

分析解決起來(lái)無(wú)非是那幾步:

dump 堆內(nèi)存

通過(guò) MAT、YourKit、JProfiler 、IDEA Profiler 等一系列工具分析dump文件

找到占用內(nèi)存最多、最大的對(duì)象,看看是哪個(gè)小可愛(ài)干的

分析代碼,嘗試優(yōu)化代碼、減少對(duì)象創(chuàng)建

增加 JVM 堆內(nèi)存、限制請(qǐng)求數(shù)、線程數(shù)、增加節(jié)點(diǎn)數(shù)量等

常見(jiàn)類(lèi)庫(kù)使用誤區(qū)

尤其是一些工具庫(kù),盡可能的避免每次新建對(duì)象,從而節(jié)省內(nèi)存提升性能。

大多數(shù)主流的類(lèi)庫(kù),入口類(lèi)都保證了單例線程安全,全局維護(hù)一份即可

舉一些常見(jiàn)的錯(cuò)誤使用例子:

Apache HttpClient

CloseableHttpClient ,這玩意相當(dāng)于一個(gè)“瀏覽器進(jìn)程”了,背后有連接池連接復(fù)用,一堆機(jī)制的輔助類(lèi),如果每次都 new 一個(gè),不僅速度慢,而且浪費(fèi)了大量資源。

比較正常的做法是,全局維護(hù)一個(gè)(或者根據(jù)業(yè)務(wù)場(chǎng)景分組,每組一個(gè))實(shí)例,服務(wù)啟動(dòng)時(shí)創(chuàng)建,服務(wù)關(guān)閉時(shí)銷(xiāo)毀:

CloseableHttpClient httpClient = HttpClients.custom()
                .setMaxConnPerRoute(maxConnPerRoute)
                .setMaxConnTotal(maxConnTotal)
                /// ...
                                 .build();

Gson

畢竟是 Google 的項(xiàng)目,入口類(lèi)自然也是實(shí)現(xiàn)了線程安全,全局維護(hù)一份 Gson 實(shí)例即可

Jackson

Jackson 作為 Spring MVC 默認(rèn)的 JSON 處理庫(kù),功能強(qiáng)大、用戶眾多,xml/json/yaml/properties/csv 各種主流格式都支持,單例線程安全自然也是 ok 的,全局維護(hù)一份 ObjectMapper 即可。

GC overhead limit exceeded

這個(gè)錯(cuò)誤比較有意思,上面的 Java heap space 是內(nèi)存徹底滿了之后,還在持續(xù)的創(chuàng)建新對(duì)象,此時(shí)服務(wù)會(huì)徹底假死,無(wú)法處理新的請(qǐng)求。

而這個(gè)錯(cuò)誤,只是表示 GC 開(kāi)銷(xiāo)過(guò)大,Collector 花了大量的時(shí)間回收內(nèi)存,但釋放的堆內(nèi)存卻很小,并不代表服務(wù)死了

此時(shí)程序處于一種很微妙的狀態(tài):堆內(nèi)存滿了(或者達(dá)到回收閾值),不停的觸發(fā) GC 回收,但大多數(shù)對(duì)象都是可達(dá)的無(wú)法回收,同時(shí) Mutator 還在低頻率的創(chuàng)建新對(duì)象。

出現(xiàn)這個(gè)錯(cuò)誤,一般都是流量較低的場(chǎng)景,有太多常駐的可達(dá)對(duì)象無(wú)法回收,但是吧,GC 后空閑的內(nèi)存還可以滿足服務(wù)的基本使用

不過(guò)此時(shí),已經(jīng)在頻繁的老年代GC了,老年代又大對(duì)象又多、在現(xiàn)有的回收算法下,GC 效率非常低并切資源占用巨大,甚至?xí)霈F(xiàn)把 CPU 打滿的情況。

出現(xiàn)這個(gè)錯(cuò)誤的時(shí)候,從監(jiān)控角度看起來(lái)可能是這個(gè)樣子:

請(qǐng)求量可能并不大

不停 GC,并切暫停時(shí)間很長(zhǎng)

時(shí)不時(shí)的還有新的請(qǐng)求,但響應(yīng)時(shí)間很高

CPU 利用率很高

畢竟還是堆內(nèi)存的問(wèn)題,排查思路和上面的 Java heap space 沒(méi)什么區(qū)別。

Metaspace/PermGen

Metaspace 區(qū)域里,最主要的就是 Class 的元數(shù)據(jù)了,ClassLoader 加在的數(shù)據(jù),都會(huì)存儲(chǔ)在這里。

MetaSpace 初始值很小,默認(rèn)是沒(méi)有上限的。當(dāng)利用率超過(guò)40%(默認(rèn)值 MinMetaspaceFreeRatio)會(huì)進(jìn)行擴(kuò)容,每次擴(kuò)容一點(diǎn)點(diǎn),擴(kuò)容也不會(huì)直接 FullGC。

比較推薦的做法,是不給初始值,但限制最大值:

-XX:MaxMetaspaceSize=

不過(guò)還是得小心,這玩意滿了后果很?chē)?yán)重,輕則 Full GC,重則 OOM:

java.lang.OutOfMemoryError: Metaspace

排查 MetaSpace 的問(wèn)題,主要思路還是追蹤 Class Load數(shù)據(jù),比較主流的做法是:

通過(guò) Arthas 之類(lèi)的工具,查看 ClassLoader、loadClassess 的數(shù)據(jù),分析數(shù)量較多的 ClassLoader 或者 Class

打印每個(gè) class 的加載日志:-XX:+TraceClassLoading -XX:+TraceClassUnloading

下面介紹幾個(gè)常見(jiàn)的,可能導(dǎo)致 MetaSpace 增長(zhǎng)的場(chǎng)景:

反射使用不當(dāng)

JAVA 里的反射,性能是非常低的,以反射的對(duì)象必須得緩存起來(lái)。尤其是這個(gè)Method對(duì)象,如果在并發(fā)的場(chǎng)景下,每次都獲取新的 Method,然后 invoke 的話,用不了多久 MetaSpace 就給你打爆!

簡(jiǎn)單的說(shuō),并發(fā)場(chǎng)景下,Method.invoke 會(huì)重復(fù)的動(dòng)態(tài)創(chuàng)建 class,從而導(dǎo)致 MetaSpace 區(qū)域增長(zhǎng),具體分析可以參考笨神的文章《從一起GC血案談到反射原理》。

用反射時(shí),盡可能的用成熟的工具類(lèi),Spring的、Apache的都可以。它們都內(nèi)置了reflection相關(guān)對(duì)象的緩存,功能又全性能又好,足以解決日常的使用需求。

一些 Agent 的 bug

一些 Java Agent,靜態(tài)的和運(yùn)行時(shí)注入的都算。基于 Instrumentation 這套 API 做了各種增強(qiáng),一會(huì) load 一會(huì) redefine 一會(huì)remove的,如果不小心出現(xiàn) BUG,也很容易生成大量動(dòng)態(tài)的 class,從而導(dǎo)致 metaspace 打滿。

動(dòng)態(tài)代理問(wèn)題

像 Spring 的 AOP ,也是基于動(dòng)態(tài)代理實(shí)現(xiàn)的,不管是 CgLib 還是 JDK Proxy,不管是 ASM 還是 ByteBuddy。最終的結(jié)果都逃不開(kāi)動(dòng)態(tài)創(chuàng)建、加載 Class,有這兩個(gè)操作,那 Metaspace 必定受影響。

Spring 的 Bean 默認(rèn)是 singleton 的,如果配置為 prototype,那么每次 getBean 就會(huì)創(chuàng)建新的代理對(duì)象,重新生成動(dòng)態(tài)的 class、重新 define,MetaSpace 自然越來(lái)越大。

Code Cache

Code Cache 區(qū)域,存儲(chǔ)的是 JIT 編譯后的熱點(diǎn)代碼緩存(注意,編譯過(guò)程中使用的內(nèi)存不屬于 Code cache),也屬于 non heap 。

如果 Code cache 滿了,你可能會(huì)看到這么一條日志:

Server VM warning: CodeCache is full. Compiler has been disabled.

此時(shí) JVM 會(huì)禁用 JIT 編譯,你的服務(wù)也會(huì)開(kāi)始變慢。

Code Cache 的上限默認(rèn)比較低,一般是240MB/128MB,不同平臺(tái)可能有所區(qū)別。

可以通過(guò)參數(shù)來(lái)調(diào)整 Code Cache 的上限:

-XX:ReservedCodeCacheSize=

只要盡量避免過(guò)大的Class、Method ,一般也不太會(huì)出現(xiàn)這個(gè)區(qū)域被打滿的問(wèn)題,默認(rèn)的 240MB/128MB 也足夠了

Direct Memory

Direct Memory 區(qū)域,一般稱之為直接內(nèi)存,很多涉及到 磁盤(pán)I/O ,Socket I/O 的場(chǎng)景,為了“Zero Copy”提升性能都會(huì)使用 Direct Memory。

就比如 Netty ,它真的是把 Direct Memory 玩出了花(有空寫(xiě)一篇 Netty 內(nèi)存管理分析)……

使用 Direct Memory時(shí),相當(dāng)于直接繞過(guò) JVM 內(nèi)存管理,調(diào)用 malloc() 函數(shù),體驗(yàn)手動(dòng)管理內(nèi)存的樂(lè)趣~

不過(guò)吧,這玩意使用比較危險(xiǎn),一般都配合 Unsafe 操作,一個(gè)不小心地址讀寫(xiě)的地址錯(cuò)誤,就能得到一個(gè) JVM 給你的驚喜:

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007ffdbd5d19b4, pid=1208, tid=0x0000000000002ee0
#
# JRE version: Java(TM) SE Runtime Environment (8.0_301-b09) (build 1.8.0_301-b09)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.301-b09 mixed mode windows-amd64 compressed oops)  
# Problematic frame:
# C  [msvcr100.dll+0x119b4]
# 
# No core dump will be written. Minidumps are not enabled by default on client versions of Windows
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

更多的解釋,可以參考我這篇《Java中的Heap Buffer與Direct Buffer

這個(gè) Direct Memory 區(qū)域,默認(rèn)是無(wú)上限的,但為了防止被 OS Kill,還是會(huì)限制一下,給個(gè)256MB或者更小的值,防止內(nèi)存無(wú)限增長(zhǎng):

-XX:MaxDirectMemorySize=

如果 Direct Memory 達(dá)到 MaxDirectMemorySize 并且無(wú)法釋放時(shí),就會(huì)得到一個(gè) OOM錯(cuò)誤:

java.lang.OutOfMemoryError: Direct buffer memory

Linux OOM Killer

跳出 JVM 內(nèi)存管理之后,當(dāng) OS 內(nèi)存耗盡時(shí),Linux 會(huì)選擇內(nèi)存占用最多,優(yōu)先級(jí)最低或者最不重要的進(jìn)程殺死。

一般在容器里,主要的進(jìn)程就是肯定是我們的 JVM ,一旦內(nèi)存滿,第一個(gè)殺的就是它,而且還是 kill -TERM (-9)信號(hào),打你一個(gè)猝不及防。

如果 JVM 內(nèi)存參數(shù)配置合理,遠(yuǎn)低于容器內(nèi)存限制,還是出現(xiàn)了 OOM Killer 的話,那么恭喜你,大概率是有什么 Native 內(nèi)存泄漏。

這部分內(nèi)存,JVM 它還管不了。

除了 JVM 內(nèi)部的 Native 泄漏 BUG 這種小概率事件外,大概率是你引用的第三方庫(kù)導(dǎo)致的。

這類(lèi)問(wèn)題排查起來(lái)非常麻煩,畢竟在 JVM 之外,只能靠一些原生的工具去分析。

而且吧,這種動(dòng)不動(dòng)就要 root 權(quán)限的工具,可是得領(lǐng)導(dǎo)審批申請(qǐng)權(quán)限的……排查成本真的很高

排查 Native 內(nèi)存的基本的思路是:

pmap 查看內(nèi)存地址映射,定位可疑內(nèi)存塊、分析內(nèi)存塊數(shù)據(jù)

strace 手動(dòng)追蹤進(jìn)程系統(tǒng)調(diào)用,分析內(nèi)存分配的系統(tǒng)調(diào)用鏈路

更換jemalloc/tcmalloc之類(lèi)的內(nèi)存分配器(或者 async-profiler有個(gè)支持native 分析的分支)追蹤malloc的調(diào)用鏈路

目前最常見(jiàn)的 Native 內(nèi)存泄漏場(chǎng)景,是 JDK 的 Inflater/Deflater 這倆臥龍鳳雛,功能是提供 GZIP 的壓縮、解壓,在默認(rèn) glibc 的 malloc 實(shí)現(xiàn)下,很容易出現(xiàn)“內(nèi)存泄漏”。如果出現(xiàn) Native 內(nèi)存泄漏,可以先看看應(yīng)用里有沒(méi)有 GZIP 相關(guān)操作,說(shuō)不定有驚喜。


好了,各類(lèi)風(fēng)格的 OOM 都感受完了,到底哪一個(gè)更能打動(dòng)你呢?

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2974

    瀏覽量

    105139
  • JVM
    JVM
    +關(guān)注

    關(guān)注

    0

    文章

    158

    瀏覽量

    12260
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    別動(dòng),人類(lèi)已經(jīng)超級(jí)樂(lè)視包圍了

    漂著樂(lè)視云……人類(lèi)已經(jīng)超級(jí)樂(lè)視包圍了。##樂(lè)視打智能手機(jī)這張牌令 “平臺(tái)+內(nèi)容+終端+應(yīng)用”的生態(tài)模式看起來(lái)更加完備。
    發(fā)表于 04-15 09:26 ?1434次閱讀

    基于 GPU 渲染的高性能空間包圍計(jì)算

    空間包圍檢測(cè)在計(jì)算機(jī)圖形學(xué)、虛擬仿真、工業(yè)生產(chǎn)等有著廣泛的應(yīng)用。
    的頭像 發(fā)表于 02-18 10:47 ?726次閱讀
    基于 GPU 渲染的高性能空間<b class='flag-5'>包圍</b>計(jì)算

    Matlab Out of memory問(wèn)題總結(jié)(1)

    首先,要聲明,matlab自帶的Help才是最權(quán)威的Matlab學(xué)習(xí)資料,如果有時(shí)間好好學(xué)習(xí)一下或是可以高效的使用的話,一定受益匪淺!比如說(shuō)像Out of Memory這個(gè)問(wèn)題,最開(kāi)始
    發(fā)表于 02-24 15:26

    Matlab Out of memory問(wèn)題總結(jié)(2)

    arrayY1000x10004004000double array (sparse)5.使用pack命令當(dāng)內(nèi)存分為很多碎片以后,其實(shí)本身可能有很大的空間,只是沒(méi)有作構(gòu)的連續(xù)空間即大的Block而已。如果此時(shí)Out
    發(fā)表于 02-24 15:27

    vivado建立AD9361配置工程總是彈出out of memory錯(cuò)誤

    運(yùn)行system_project.tcl之后一段時(shí)間總是彈出out of memory 錯(cuò)誤。但是改用vivado2013.4版本使用相應(yīng)的hdl_2014_r1則不存在問(wèn)題,請(qǐng)問(wèn)vivado2014.2遇到的out of
    發(fā)表于 10-08 16:37

    matlab打開(kāi)1G以上的數(shù)據(jù)出現(xiàn)out of memory error

    用matlab打開(kāi)1G以上的數(shù)據(jù)出現(xiàn)out of memory error誰(shuí)遇到過(guò)這個(gè)情況么 有什么有效的解決辦法么
    發(fā)表于 04-17 06:36

    C6748_NandWrite.out文件報(bào)錯(cuò)

    verify target memory and memory map。看了下,0xC0000000是DDR2的地址。想問(wèn)下C6748_NandWrite.
    發(fā)表于 05-29 15:38

    PSoC Creator怎么進(jìn)行嵌入式設(shè)計(jì)?

    嵌入式系統(tǒng)是計(jì)算設(shè)備硬件中嵌入軟件作為其核心組件的應(yīng)用。我們身邊現(xiàn)在已經(jīng)嵌入式系統(tǒng)包圍了,這些產(chǎn)品能為我們的生活帶來(lái)各種方便乃至奢華的功能,包括移動(dòng)手持設(shè)備、洗衣機(jī)、微波爐、ATM 機(jī)、空調(diào)等等。由于某些特定的應(yīng)用要求,工程師必須以不同于其它設(shè)計(jì)類(lèi)型的特定方法進(jìn)行嵌入式
    發(fā)表于 04-13 07:04

    運(yùn)行時(shí)出現(xiàn)out of memory是堆配置原因嗎

    [C674X_0] ti.sy***ios.heaps.HeapMem: line 307: out of memory: handle=0xc01d7118, size
    發(fā)表于 04-22 14:15

    大佬們,Error (114016): Out of memory in module quartus_map.exe (20434 megabytes used)怎么解決

    Error (114016): Out of memory in module quartus_map.exe (20434 megabytes used)Error (293007
    發(fā)表于 06-01 07:31

    pads 9.5 / VX2.11 Out of memory

    在win10/win11下使用PADS layout時(shí),報(bào)錯(cuò)‘’Out of memory‘’,或者報(bào)錯(cuò)‘’數(shù)據(jù)庫(kù)嚴(yán)重錯(cuò)誤編號(hào) 2010‘’已經(jīng)嘗試過(guò):1.加大內(nèi)存條內(nèi)存,無(wú)法解決2.加大虛擬內(nèi)存
    發(fā)表于 03-25 18:58

    用VScode將kmodel和代碼制作成的bin文件燒入K210開(kāi)發(fā)板中出現(xiàn)“iomem malloc out of memory”是什么情況?

    用VScode將kmodel和代碼制作成的bin文件燒入K210開(kāi)發(fā)板中出現(xiàn)“iomem malloc out of memory”是什么情況?
    發(fā)表于 09-13 06:35

    PCB設(shè)計(jì):盤(pán)中孔工藝,到底有多大價(jià)值?

    不知道大家在畫(huà)PCB的時(shí)候會(huì)不會(huì)遇到這樣的情況:芯片的引腳太密,某個(gè)引腳想要走線出去但是完全包圍了,尤其是在BGA封裝的芯片中。例如下圖中的U1_B7引腳就沒(méi)有辦法再走線出去,四周都被包圍了
    發(fā)表于 10-24 11:25 ?2444次閱讀

    盤(pán)中孔工藝有多大價(jià)值?先前為什么在市場(chǎng)上沒(méi)有普及呢?

    不知道大家在畫(huà)PCB的時(shí)候會(huì)不會(huì)遇到這樣的情況:芯片的引腳太密,某個(gè)引腳想要走線出去但是完全包圍了,尤其是在BGA封裝的芯片中。例如下圖中的U1_B7引腳就沒(méi)有辦法在走線出去,四周都被包圍了
    的頭像 發(fā)表于 11-15 09:47 ?1777次閱讀

    宏景智駕亮相WNAT-CES展:完蛋智駕友商包圍了

    11月10日-12日,2023年世界新汽車(chē)技術(shù)合作生態(tài)展(WNAT-CES)在江蘇蘇州昆山國(guó)際會(huì)展中心隆重舉辦,宏景智駕攜旗下系列產(chǎn)品亮相本次展會(huì)。 此次宏景智駕展出的產(chǎn)品包括支持ADAS功能的智能攝像頭產(chǎn)品以及支持L2+級(jí)智能駕駛功能實(shí)現(xiàn)的系列域控制器產(chǎn)品,目前這些產(chǎn)品皆已在國(guó)內(nèi)主流車(chē)企的暢銷(xiāo)車(chē)型上實(shí)現(xiàn)量產(chǎn)交付。 作為全棧智能駕駛研發(fā)公司,在軟件層面,宏景智駕在行車(chē)、泊車(chē)以及行泊一體系統(tǒng)開(kāi)發(fā)領(lǐng)域也有深厚的積累。此次展會(huì)也是集中展示了宏
    的頭像 發(fā)表于 11-14 15:10 ?612次閱讀
    宏景智駕亮相WNAT-CES展:<b class='flag-5'>完蛋</b>!<b class='flag-5'>我</b><b class='flag-5'>被</b>智駕友商<b class='flag-5'>包圍了</b>!
    真人百家乐怎么玩| 豪享博百家乐官网的玩法技巧和规则 | 澳门玩百家乐的玩法技巧和规则| 百家乐官网1个人| 大发888战神娱乐| 百家乐官网能破解| 宝马会网上娱乐| 百家乐服务区| 百家乐官网庄家闲| 政和县| 大发888注册账号| 手机百家乐官网能兑换现金棋牌游戏| 德州扑克游戏网站| 百家乐园选百利宫| 博九百家乐官网的玩法技巧和规则 | 景洪市| 威尼斯人娱乐场官网326369| 百家乐软件l柳州| 優博百家乐官网客服| 南充市| 大发888充值平台| 百家乐赌场大全| 聚龍社百家乐官网的玩法技巧和规则 | 百家乐投注打三断| 百家乐官网真人游戏娱乐场| 真人百家乐官网打法| 大发888作弊| 大丰收百家乐的玩法技巧和规则 | 百家乐官网庄闲排列| 大城县| bet365体育在线15| E世博百家乐的玩法技巧和规则 | 明升论坛| 大发888娱乐85战神版| 百家乐网上赌有作假吗| 属蛇和属马合作做生意谁吃亏| 澳门百家乐官网庄闲和| 开阳县| 德州扑克官网| 威尼斯人娱乐城平台打不开| 百家乐园百利宫娱乐城怎么样百家乐园百利宫娱乐城如何 |