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

歡迎訪問 生活随笔!

生活随笔

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

java

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

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

問題描述

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

排查過程

  • 通過對Java堆進行分析,發現數據量較大的實例類型為char[],其中最大的一個char[]實例大小為127MB,對其內容進行分析,發現與某接口的方法有關。
  • 進一步分析發現,該接口在某一參數的情況下,就會產生這種大對象。同時這個是一個局部變量。
  • 檢查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,將觸發CMS的MajorGC。
    (3)我們知道CMS的GC有部分過程是可以與用戶線程同時執行的,假如在這個過程中,用戶線程產生的對象大小占滿老年代剩余的30%,那么CMS并發模式的GC就失敗了(concurrent mode failure)。
    (4)當CMS的并發GC失敗后,將使用Serial Old的串行GC重新執行。
    (5)Serial Old的GC是會全過程Stop The World的,也就是造成長時間停頓。
    5. 為了驗證上述結論,開啟GC日志后對此場景進行復現。

    復現記錄

  • 修改-Xms5120m避免擴容,增加-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/data/gc.log
  • 重啟發現FullGC很短的時間內就發生了4次,觀察發生FullGC前后,出現了MC、CCSC增大的情況。得出這2部分內存的初始值過小。
    (1)MC:方法區大小。按目前使用量,可調整為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

    可以發現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區偶爾會出現減少,但馬上又會被迅速占滿,old區始終維持占滿狀態)。
    6.清理cookie后,重新登錄,出現與之前情況一致的無法登錄的現象。
    7.執行dump:live,得到7.9G文件。(此處與上次情況不同,上次執行完后,old區域就被回收掉了,而dump文件也只有142M。另外,上次tomcat日志中有出現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.第一次出現Full GC (Allocation Failure)在1544.618。
    2.伴隨出現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分鐘。

    堆分析

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

    解決辦法

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

    總結

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

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