日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

记录一次大对象导致的Java堆内存溢出问题

發(fā)布時間:2024/2/28 java 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 记录一次大对象导致的Java堆内存溢出问题 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

問題描述

前幾天早上出現(xiàn)一后臺項目無法登陸的情況,排查發(fā)現(xiàn)新生代和老年代都占用100%,FullGC次數(shù)大概有100多次,最終出現(xiàn)OOM。
重啟Tomcat后,至13點,FullGC的次數(shù)達到31次。

排查過程

  • 通過對Java堆進行分析,發(fā)現(xiàn)數(shù)據(jù)量較大的實例類型為char[],其中最大的一個char[]實例大小為127MB,對其內容進行分析,發(fā)現(xiàn)與某接口的方法有關。
  • 進一步分析發(fā)現(xiàn),該接口在某一參數(shù)的情況下,就會產(chǎn)生這種大對象。同時這個是一個局部變量。
  • 檢查JVM配置如下:
  • -server -Xrs -Xmx5120m -Xms1536m -Xmn512m -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -XX:SurvivorRatio=8

    可知,新生代中Survivor占51.2MB,無法放入127MB的char[]實例。

    故這種對象如果在第一次MinorGC時存活,它將無法進入survivor,而會提前轉移到老年代。
    4. 那么,這類大小為127MB的局部變量為什么在MajorGC時能夠存活?推測原因如下:
    (1)第3點所述的熬過一輪MinorGC提前進入老年代的對象不斷增加,直至占滿老年代的70%。
    (2)這時由于CMSInitiatingOccupancyFraction=70,將觸發(fā)CMS的MajorGC。
    (3)我們知道CMS的GC有部分過程是可以與用戶線程同時執(zhí)行的,假如在這個過程中,用戶線程產(chǎn)生的對象大小占滿老年代剩余的30%,那么CMS并發(fā)模式的GC就失敗了(concurrent mode failure)。
    (4)當CMS的并發(fā)GC失敗后,將使用Serial Old的串行GC重新執(zhí)行。
    (5)Serial Old的GC是會全過程Stop The World的,也就是造成長時間停頓。
    5. 為了驗證上述結論,開啟GC日志后對此場景進行復現(xiàn)。

    復現(xiàn)記錄

  • 修改-Xms5120m避免擴容,增加-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/data/gc.log
  • 重啟發(fā)現(xiàn)FullGC很短的時間內就發(fā)生了4次,觀察發(fā)生FullGC前后,出現(xiàn)了MC、CCSC增大的情況。得出這2部分內存的初始值過小。
    (1)MC:方法區(qū)大小。按目前使用量,可調整為75MB。
    (2)CCSC:壓縮類空間大小。按目前使用量,可調整為10MB。
  • 調用一次出問題的接口,調用前后GC情況
  • S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 52416.0 52416.0 52416.0 0.0 419456.0 17534.6 4718592.0 66465.3 72012.0 71106.0 7560.0 7334.3 30 2.209 4 0.116 2.324 52416.0 52416.0 0.0 52416.0 419456.0 82050.7 4718592.0 264498.1 72908.0 71710.7 7688.0 7375.7 31 2.471 4 0.116 2.586

    可以發(fā)現(xiàn)Eden增加65MB,老年代增加200MB。
    4. 重復多次請求接口后,新生代、老年代均被占滿。

    S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 52416.0 52416.0 52416.0 0.0 419456.0 419456.0 4718592.0 4718592.0 75084.0 73600.4 7816.0 7422.0 607 40.282 42 203.704 243.986

    5.此時關閉所有頁面,等待10分鐘左右,JVM占滿情況仍然無法恢復(Eden和survivor區(qū)偶爾會出現(xiàn)減少,但馬上又會被迅速占滿,old區(qū)始終維持占滿狀態(tài))。
    6.清理cookie后,重新登錄,出現(xiàn)與之前情況一致的無法登錄的現(xiàn)象。
    7.執(zhí)行dump:live,得到7.9G文件。(此處與上次情況不同,上次執(zhí)行完后,old區(qū)域就被回收掉了,而dump文件也只有142M。另外,上次tomcat日志中有出現(xiàn)OOM的日志,本次沒有)
    8.重啟該tomcat,截止重啟前,GC情況如下:

    S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 52416.0 52416.0 52416.0 0.0 419456.0 419456.0 4718592.0 4718592.0 75084.0 73653.9 7816.0 7423.7 607 40.282 129 1054.540 1094.822

    GC分析

    GC日志

    1.第一次出現(xiàn)Full GC (Allocation Failure)在1544.618。
    2.伴隨出現(xiàn)concurrent mode failure,這種提示代表無法在老年代填滿之前完成垃圾回收,或者一個新的對象無法在老年代的剩余空間完成分配,這時程序會停止所有線程來完成GC。原文如下:

    if the concurrent collector is unable to finish reclaiming the unreachable objects before the tenured generation fills up, or if an allocation cannot be satisfied with the available free space blocks in the tenured generation, then the application is paused and the collection is completed with all the application threads stopped

    3.結束時間為2723.551,即從老年代占滿到被重啟間隔1179秒,約20分鐘。

    堆分析

    下圖為復現(xiàn)過程的dump文件,大小最大的已經(jīng)不是char[],不過前幾個過大的對象均為調用上述接口中的局部變量。

    解決辦法

    1.優(yōu)化JVM啟動參數(shù)
    (1)調整堆內存初始值為-Xms5120m避免擴容
    (2)調整新生代大小為-Xmn1536m
    (3)CMSInitiatingOccupancyFraction=60
    (4)方法區(qū)大小調整為100MB
    (5)壓縮類空間大小調整為15MB
    2.對該接口實現(xiàn)進行優(yōu)化
    3.JVM參數(shù)調整后跟蹤FullGC情況

    總結

    以上是生活随笔為你收集整理的记录一次大对象导致的Java堆内存溢出问题的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。