JVM調(diào)優(yōu)之解決內(nèi)存溢出,節(jié)省60G內(nèi)存,避免Full GC

目錄

  • 背景
  • 實踐
    • 堆內(nèi)內(nèi)存查看分析
    • 堆外內(nèi)存限制處理
    • 修改jvm參數(shù)之后效果
    • 進一步分析堆內(nèi)占用高的業(yè)務(wù)代碼
    • 番外之mat分析查看內(nèi)存對象值
  • 參考文章

背景

  • 線上服務(wù)目前使用了128G內(nèi)存,然而在大部分時間里,內(nèi)存占用都超過120G,甚至接近滿載。這導致了服務(wù)不時出現(xiàn)崩潰的情況,我們可以從內(nèi)核層面看到相關(guān)的kill命令。在提供的java應(yīng)用占用內(nèi)存截圖中,我們可以看到大部分內(nèi)存被Java應(yīng)用占用,優(yōu)化前的JVM參數(shù)僅設(shè)置了最大堆和最小堆(分別為100G),其他參數(shù)均為默認設(shè)置。Java應(yīng)用占用的內(nèi)存接近120G,這是導致服務(wù)崩潰的罪魁禍首


    所有應(yīng)用內(nèi)存占用.png

    Java應(yīng)用內(nèi)存占用.png
  • 在進行了深入的JVM調(diào)優(yōu)之后,我們成功地節(jié)省了高達60G的內(nèi)存,并徹底解決了頻繁發(fā)生的Full GC問題?,F(xiàn)在,應(yīng)用程序?qū)⒛軌蚋椒€(wěn)地運行,同時減少了不必要的內(nèi)存浪費和性能波動。這意味著更高的應(yīng)用程序性能和更佳的客戶體驗,以及更穩(wěn)定的業(yè)務(wù)連續(xù)性


實踐

堆內(nèi)內(nèi)存查看分析

  • jps -l查看java應(yīng)用pid
  • jstat -gcutil pid 1000 20 每秒輸出gc情況,輸出20次,半天就有199次full gc了,每次時間平均10多秒


    gc情況.png
  • jmap -heap pid指令查看內(nèi)存, 這里需要使用跟運行jvm一樣的用戶,比如我這邊root用戶運行的jvm,那么我也得用root用戶查看


    簡單查看內(nèi)存占用.png
  • 從簡單查看內(nèi)存占用截圖,分析java年輕代老年代占用50-60G了,這部分隨著接口的處理,老年代和年輕代還會漲


    老年代增長趨勢.png

    年輕代增長趨勢.png
  • 從之前優(yōu)化Jvm參數(shù)經(jīng)驗來說,內(nèi)存一直都很告的原因是堆內(nèi)內(nèi)存設(shè)置的過大(100G),導致堆內(nèi)回收年輕代和老年代比較晚,然后又有新的對象過來,導致整體內(nèi)存占用比較大,最終就超過服務(wù)內(nèi)存上限被系統(tǒng)kill掉

堆外內(nèi)存限制處理

  • 堆外內(nèi)存主要用于一些需要大量數(shù)據(jù)操作或避免在Java堆內(nèi)進行數(shù)據(jù)拷貝的場景,ByteBuffer.allocateDirect類似這種寫法,或者netty都有可能堆外,這塊可以考慮有些定時任務(wù)抽出去。避免那么大堆內(nèi)和大堆外一起,就容易oom,Apache Commons Compress 庫中的 Compressor 和 Decompressor 類使用了 DirectBuffer 類,該類是 Apache Commons IO 庫中的一個類,提供了對堆外內(nèi)存的訪問
  • 標準的jvm參數(shù)里面應(yīng)該包含堆外,元數(shù)據(jù)空間限制,這里加上堆外,元數(shù)據(jù)空間限制,具體jvm參數(shù)設(shè)置為(按照這個配置之后生成的oom文件是hprof格式的):
-Xms49152m -Xmx49152m -XX:MaxGCPauseMillis=200 -XX:+UseG1GC -XX:MaxDirectMemorySize=1024m -XX:-OmitStackTraceInFastThrow -XX:MinHeapFreeRatio=30 -XX:MaxHeapFreeRatio=50 -XX:MetaspaceSize=1024m -XX:MaxMetaspaceSize=1024m -XX:MinMetaspaceFreeRatio=0 -XX:MaxMetaspaceFreeRatio=100 -XX:G1ReservePercent=15 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=64M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/xxx -Xloggc:/xxxxx/gc.log -XX:ParallelGCThreads=8 -XX:ConcGCThreads=8 -XX:+HeapDumpOnOutOfMemoryError
  • 對比之前的jvm參數(shù),增加了很多jvm參數(shù),由之前使用的并行g(shù)c垃圾回收器改為G1垃圾回收器,增加堆外內(nèi)存限制,元數(shù)據(jù)空間大小限制,以及減小最大最小堆,以使其及時回收對應(yīng)內(nèi)存,而不至于oom
  • 這邊測試過調(diào)小jvm堆到1G之后oom會生成對應(yīng)hprof文件,默認就是hprof文件,命名是用pid后綴


    oom生成對應(yīng)hprof文件.png
  • 下載下來本地mat分析就能看到oom具體哪個對象占用大了,這邊本地下載文件是用XShell下載的,這塊java堆設(shè)置的比linux服務(wù)器內(nèi)存小不少,生成oom的hprof文件也比較容易


    mat分析oom文件.png

修改jvm參數(shù)之后效果

  • java占用的內(nèi)存明顯降低,運行也比較穩(wěn)定,gc垃圾回收也正常,注意看著ps不包含堆外,ps命令查看的進程占用內(nèi)存大小只包含了JVM進程使用的堆內(nèi)存。堆外內(nèi)存是JVM進程使用的系統(tǒng)內(nèi)存,無法通過ps命令直接查看。要查看JVM進程使用的堆外內(nèi)存,可以使用pmap等命令,對比nmt


    修改jvm后運行一段時間.png
  • gc停頓時間符合預(yù)期,gc停掉時間符合預(yù)期2截圖顯示沒有full gc了


    gc停掉時間符合預(yù)期.png

    gc停掉時間符合預(yù)期2.png
  • 整體機器內(nèi)存使用也比較穩(wěn)定,不會到128G而被kill掉


    整體機器使用內(nèi)存.png

進一步分析堆內(nèi)占用高的業(yè)務(wù)代碼

  • 使用jmap -dump:format=b,file=/my.hprof pid,因為dump下來有大幾十G,本地機器內(nèi)存才16G,要本地mat分析內(nèi)存dump情況難度很高,這邊就使用scp命令從線上環(huán)境拉去hprof文件到測試環(huán)境,因為測試環(huán)境上也是128G比較容易分析,不會像本地一樣卡住
  • 在測試環(huán)境安裝mat linux使用MAT分析dump文件,然后使用分析命令
nohup ./mat/ParseHeapDump.sh my0923.hprof  org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat &
  • 然后有幾份zip文件不到1M的,下載到本地,解壓瀏覽器打開
  • 看到有個Map對象占用了20多個G,罪魁禍首就是分析2截圖的map對象,這個功能是將數(shù)據(jù)幾十萬數(shù)據(jù)全表加載到內(nèi)存,這么大的對象就算是內(nèi)存運行map查詢也不會太快,后面計劃使用其他架構(gòu)比如ES代替全表加載到內(nèi)存的功能,整體內(nèi)存占用還能下降不少


    分析.png

    分析2.png
  • 注意這里用linux服務(wù)器上mat分析只能分析大概,細致的還是要本地mat分析合適,所以本地機器內(nèi)存更大會更容易分析些,這邊下載大內(nèi)存的hprof文件,可以考慮先zip,然后按照sz方法從服務(wù)器下載超過4g文件方法分析文件,到本地合并下載,因為xshell限制文件sz到4G
  • 當然也可以利用linux大內(nèi)存的服務(wù)mat分析后, 將生成好的文件逐個下下來,本地分析


    mat生成.png

番外之mat分析查看內(nèi)存對象值

  • 順帶實踐下實戰(zhàn)總結(jié)|一次訪問Redis延時高問題排查與總結(jié)(續(xù))用工具分析查看線上運行的內(nèi)存對象值
  • dump下堆文件hprof后,打開mat,使用Histogram,搜索redis相關(guān)配置


    堆分析.png
  • 選擇"Merge ... Path to GC Root"->"exclude weak/soft references",之后就可以看redis連接創(chuàng)建銷毀數(shù)目了


    堆分析2.png

    堆分析3.png
  • 或者His展示時List Object右鍵,記得拉長一些


    分析3.png

    分析4.png

參考文章

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

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

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