“喜提”一個P2級故障—CMSGC太頻繁,你知道這是什麼鬼?

来源:https://www.cnblogs.com/StarbucksBoy/archive/2022/12/26/17005680.html
-Advertisement-
Play Games

前段時間收到線上一些列告警,內容是CMSGC太頻繁。那接下來這篇文章我會告訴你:什麼是CMSGC太頻繁;整個排查過程與你分享;以及一些規避手段。 ...


大家好,我是陶朱公Boy。

背景

今天跟大家分享一個前幾天線上上碰到的一個GC故障— "CMSGC太頻繁"。

不知道大家看到這條告警內容後,是什麼感觸?我當時是一臉懵逼的,一萬個為什麼縈繞心頭。

什麼是CmsGc?CmsGc太頻繁又是什麼意思?什麼情況下會觸發CMSGC太頻繁這種告警?要怎麼樣去找到那個被頻繁創建的對象?最後又需要怎麼規避?

接下來這篇文章我會來回答一下:什麼是CMSGC太頻繁;整個排查過程與你分享;最後我們一起探討一下一些規避手段。

什麼是CMSGC太頻繁

首先我覺得還是有必要解釋清楚什麼是CMSGC太頻繁這個術語,相信不少小伙伴也是比較關心的。

如果你聽過垃圾搜集器中有一款名為CMS垃圾搜集器,那就好理解了,所謂的CMSGC太頻繁意思是說CMS垃圾搜集器在當下時間視窗垃圾收集的動作頻次太快(平時老半天才回收一次或幾次垃圾對象,現在可能一分鐘就需要回收多次),大致就是這個意思。

關於CMS垃圾收集器的說明:

上述這張圖中共有7種不同的垃圾搜集器,用連線表示它們彼此之間的搭配使用。
分割線上面部分是年輕代區域,像Serial、ParNew、Parallel Scavenge這三款垃圾收集器是用來搜集年輕代記憶體區域的垃圾收集器。
分割線下麵部分是老年代區域,像CMS、Serial Old、Parallel Old這三款垃圾收集器用來收集老年代區域的垃圾收集器。
在實際線上配置場景中,我們一般通過CMS+ParNew,採用分代收集(parNew垃圾收集器用來收集年輕代區域,Cms垃圾收集器用來收集老年代區域)來進行配置。

所以說CMS垃圾收集器是一款作用於老年代區域的垃圾收集器。

關於CMS+ParNew垃圾搜集器的配置說明:大家如果在VM啟動配置參數中做如下配置:-XX:+UseConcMarkSweepGC.該配置項首先是激活CMS收集器(作用於老年代)。之後-XX:UseParNewGC會自動開啟,意味著年輕代將使用多線程並行垃圾收集器parNew進行回收。

 

原因分析

上文中,我給大家解釋了CMSGC太頻繁的意思。其實就是CMS垃圾搜集器對作用於老年代的垃圾對象進行回收,但頻次太高,所以才觸發了告警。
接下來給大家介紹一下引起對象進入老年代的幾種場景,然後再給大家介紹一下幾種觸發CMSGC的情況。大家需要先搞明白有哪些情況對象會進入老年代,又達到什麼標準作用於老年代的垃圾收集線程開始會對垃圾對象進行回收。
▲對象進入老年代的幾種情況
  • 新生代因為垃圾回收之後,因為存活對象太多,導致Survivor空間放不下,部分對象會進入老年代
  • 大對象直接進入老年代
這裡的大對象是指那些需要大量連續空間的JAVA對象,比如那種很長的字元串或數組對象。
  • 長期存活的對象將進入老年代
對象在Eden出生,並經過第一次YGC後任然存活,並且能被Survivor空間容納,將被移動到Survivor空間中,並且對象年齡設為1。對象在Survivor空間每熬過一次YGC,年齡就增加一歲,如果達到15(預設)歲,對象就會進入老年代。
  • 動態對象年齡判斷
這點是對長期存活的對象進入老年代的補充。
其實不一定要必須滿足所謂的存活對象年齡達到15歲才能進入老年代。如果一次YGC後,儘管Survivor區域有空間能容納存活對象,但這批存活對象恰好存活的年齡相同,且加起來的大小總和大於Survivor空間的一半,這些對象照樣會進入老年代。
▲觸發CMS垃圾收集動作的幾個時機
CMS垃圾收集動作不可能實時發生,只有滿足了相應條件,才會被觸發。以下幾點供你參考:
  • 老年代可用的連續空間小於年輕代歷次YGC後升入老年代的對象總和的平均大小,說明YGC後升入老年代的對象大小很可能超過了老年底當期可用的記憶體空間;觸發cmsgc後再進行ygc

  • ygc之後有一批對象需要放入老年代,但老年代沒有足夠的空間存放了,需要觸發一次cmsgc

  • 老年代的記憶體使用率超過92%,也要觸發OLD 過程(通過參數控制-xx:+CMSInitiatingOccupancyFraction)

排查過程

這個章節詳細也是不少小伙伴關心的內容,一旦發生了這種告警,那你肯定第一時間比較關心的內容是:到底老年代記憶體區域裡面,什麼對象會占據那麼大的空間,找到它才是當下之急。
大家其實只要記住兩個步驟,就能輕鬆找出問題對象。
步驟一:獲取堆文件
獲取堆文件,我總結瞭如下三個方式,供大家參考
  1. 配置VM參數
    -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${LOGDIR}/ 虛擬機在OOM異常之後會自動生成一份dump文件在本地 
  2. 執行jmap(Java記憶體映像工具)命令

    jdk提供的命令行工具jmap能生成堆存儲快照,jmap -dump:format=b,file=heapdump.hprof {進程ID}

  3. 阿裡開源性能診斷工具:Arthas
阿裡開源的性能診斷工具Arthas通過命令heapdump[類似jmap命令的heap dump功能]能生成堆快照文件。
詳情大家可以參考官方說明文檔:https://arthas.gitee.io/doc/heapdump.html

▲步驟二:分析堆文件
分析步驟一生成的堆文件,一般需要藉助一些工具常見的有MAT、Jvisualvm等。

接下來作者用本次告警dump下來的堆文件,用MAT工具給大家演示一下具體查找問題對象的全過程。

MAT是Memory Analyzer tool的縮寫,是一種快速,功能豐富的Java堆分析工具,能幫助你查找記憶體泄漏和減少記憶體消耗。
很多情況下,我們需要處理測試提供的hprof文件,分析記憶體相關問題,那麼MAT也絕對是不二之選。Eclipse可以下載插件結合使用,也可以作為一個獨立分析工具使用。
下載地址:eclipse.org/mat/downloa。如果安裝過程中可能會碰到版本過低的問題,需要安裝一下高版本JDK 比如11,最後設置一下安裝路徑即可。

 

打開堆文件

如果你已經成功安裝完MAT。進入首頁後就可以打開本地hprof文件了。

打開文件後,進入分析頁

底部有三個功能塊:Action、Reports、Step By Step。簡單給大家介紹一下相應內容:
  • Actions

    Histogram 列出每個類所對應的對象個數,以及所占用的記憶體大小;Dominator Tree 以占用總記憶體的百分比的方式來列舉出所有的實例對象,註意這個地方是直接列舉出的對應的對象而不是類,這個視圖是用來發現大記憶體對象的Top Consumers:按照類和包分組的方式展示出占用記憶體最大的一個對象Duplicate Classes:檢測由多個類載入器所載入的類信息(用來查找重覆的類)

  • Reports

    Leak Suspects:通過MAT自動分析當前記憶體泄露的主要原因
    Top Components:Top組件,列出大於總堆1%的組件的報告

  • Step By Step
    Component Report:組件報告,分析屬於公共根包或類載入器的對象
Histogram選項
這裡大家重點關註Histogram選項(列出每個類所對應的對象個數,以及所占用的記憶體大小)

DomainTree選項(以占用總記憶體的百分比的方式來列舉出所有的實例對象)

關註上述兩個選項基本就能找到問題對象了。

解決方案

要避免發生CMSGC太頻繁這種情況,我總結了以下2種方案:
  1. 如果你的程式代碼書寫正常,純粹是真的應用流量太大,你部署的機器沒辦法抗住這波流量,這種情況發生CMSGC太頻繁概率就很大了,甚至最終會導致OOM異常。對這種情況也只能橫向擴充機器了,以均衡流量。
  2. 如果你的機器足夠,線上流量也正常,但也發生了cmsgc太頻繁,甚至OOM異常。那大概率是你的程式代碼有問題,導致老年代區域聚集了大量垃圾對象,垃圾回收線程頻繁回收那些無用的垃圾對象,最終可能還達不到回收的理想效果,那麼這個時候你不得不分析堆裡面被大量占據的對象,看看是不是程式代碼問題導致老年代被堆滿。
    像作者文章開始出的這個案例,作者經過上述步驟分析後,發現是程式代碼問題導致有大量對象進入老年代。(作者在應用中引入了一個java8的Nashorn組件,該組件的構建過程極其複雜,內部會創建很多個對象實例,因為作者的業務流量還是比較大的,每秒2000+QPS),機器也是夠的大概10台(每台4C8G),分析發現記憶體中大量充斥著Nashorn相關代碼,經過深入分析,其實這個Nashorn實例全局單例就可以了,不需要每次方法執行都構建一個實例,因為構建過程複雜且多對象,流量一高勢必最終導致應用發生記憶體溢出等異常。

總結

OK,文章即將進入尾聲,讓我們一起來做個總結:
首先作者以一個自己親身經歷的GC故障為背景,跟大家介紹了一下什麼是CMSGC太頻繁這個術語,相信小伙伴如果下次自己碰到類似這種告警,能明白其含義。
其次作者也介紹了CMSGC太頻繁一般作用的區域是老年代記憶體區域,有幾種情況對象會從年輕代或直接進入老年代,以及老年代什麼情況下會觸發其垃圾回收動作。
然後作者也給大家介紹了該如何一步一步通過工具MAT去排查在堆文件里的問題對象。

最後我也總結了應該如何避免發生GC太頻繁甚至OOM這類異常。如果程式代碼一切正常,純粹是瞬時流量太高才導致的GC動作加快,可以考慮臨時增加伺服器實例,分攤流量。不過很多問題可能都是程式員代碼書寫不正確才導致的,這個時候你應該首先找出問題對象,然後找出頻繁創建對象的代碼塊。

本文完!


寫到最後

作為996的程式員,寫這篇文章基本都是利用工作日下班時間和周六周日雙休的時間才最終成稿,比較不易。   如果你看了文章之後但凡對你有所幫助或啟發,真誠懇請幫忙關註一下作者,點贊、在看此文。你的肯定與贊美是我未來創作最強大的動力,我也將繼續前行,創作出更加優秀好的作品回饋給大家,在此先謝謝大家了!

關註我

如果這篇文章你看了對你有幫助或啟發,麻煩點贊、關註一下作者。你的肯定是作者創作源源不斷的動力。

公眾號

裡面不僅彙集了硬核的乾貨技術、還彙集了像左耳朵耗子、張朝陽總結的高效學習方法論、職場升遷竅門、軟技能。希望能輔助你達到你想夢想之地!

公眾號內回覆關鍵字“電子書”下載pdf格式的電子書籍(JAVAEE、Spring、JVM、併發編程、Mysql、Linux、kafka、分散式等)、“開發手冊”獲取阿裡開發手冊2本、"面試"獲取面試PDF資料。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本篇繼續對 RM 中管理 NodeManager 的部分進行深入的講解。主要有三個部分:檢查 NM 是否存活;管理 NM 的黑白名單;響應 NM RPC 請求。 ...
  • 前言 一個完整的後端請求由 4 部分組成: 介面地址(也就是 URL 地址) 請求方式(一般就是 get、set,當然還有 put、delete) 請求數據(request,有 head 跟 body) 響應數據(response) Controller 主要的工作有以下幾項: 接收請求並解析參數 ...
  • path [ pɑ:θ ] 路徑 unexpected [ˌʌnɪkˈspektɪd] 不期望的 class [klɑ:s] 類 usage [ˈju:sɪdʒ] 使用 public ['p ʌblik] 公共的,公用的 version [ˈvɜ:ʃn] 版本 private ['praivit] ...
  • 一、 含義 Cookie意為“甜餅”,是由W3C組織提出,最早由Netscape社區發展的一種機制。目前Cookie已經成為標準,所有的主流瀏覽器如IE、Netscape、Firefox、Opera等都支持Cookie。 由於HTTP是一種無狀態的協議,伺服器單從網路連接上無從知道客戶身份,怎麼辦呢 ...
  • 哈嘍兄弟們,今天咱們來實現用Python來批量生成請假條,這回既學了東西又做了事情,兩不誤~ 本文就將基於一個真實的辦公案例進行講解如何提取Excel內容並創建Word 主要將涉及以下三個知識點 openpyxl 讀取 Excel 文件 python-docx 寫入 Word 文件 python-d ...
  • 1. 頁面傳值 1.1 父頁面向子頁面傳值 父頁面: /pages/xx/xxx?id=1 子頁面 // option就可以接收到父頁面傳來的值 onLoad:function(option){ } 1.2 子父頁面 子頁面 var pages = getCurrentPages() var pre ...
  • 摘要:盤點 Python 中字元串的幾個常用操作,對新手極度的友好。 本文分享自華為雲社區《盤點 Python 中字元串的常用操作,對新手極度友好》,作者:TT-千葉 。 在 Python 中字元串的表達方式有四種 一對單引號一對雙引號一對三個單引號一對三個雙引號a = ‘abc’b= “abc”c ...
  • 時光荏苒,這周日就是元旦了,我也把年終總結提上了日程。 前言 今年的年終總結我打算多寫幾篇,每篇瞄準一個方向,寫一些對大家有幫助、有啟發的內容。 初步的想法會整理三篇: 第一篇分享求職面試的經驗 第二篇分享接私活的經驗 第三篇分享一下在北京買房的經驗,作為過來人和你嘮嘮要不要在一線城市買房。 整理這 ...
一周排行
    -Advertisement-
    Play Games
  • 概述:本文代碼示例演示瞭如何在WPF中使用LiveCharts庫創建動態條形圖。通過創建數據模型、ViewModel和在XAML中使用`CartesianChart`控制項,你可以輕鬆實現圖表的數據綁定和動態更新。我將通過清晰的步驟指南包括詳細的中文註釋,幫助你快速理解並應用這一功能。 先上效果: 在 ...
  • openGauss(GaussDB ) openGauss是一款全面友好開放,攜手伙伴共同打造的企業級開源關係型資料庫。openGauss採用木蘭寬鬆許可證v2發行,提供面向多核架構的極致性能、全鏈路的業務、數據安全、基於AI的調優和高效運維的能力。openGauss深度融合華為在資料庫領域多年的研 ...
  • openGauss(GaussDB ) openGauss是一款全面友好開放,攜手伙伴共同打造的企業級開源關係型資料庫。openGauss採用木蘭寬鬆許可證v2發行,提供面向多核架構的極致性能、全鏈路的業務、數據安全、基於AI的調優和高效運維的能力。openGauss深度融合華為在資料庫領域多年的研 ...
  • 概述:本示例演示了在WPF應用程式中實現多語言支持的詳細步驟。通過資源字典和數據綁定,以及使用語言管理器類,應用程式能夠在運行時動態切換語言。這種方法使得多語言支持更加靈活,便於維護,同時提供清晰的代碼結構。 在WPF中實現多語言的一種常見方法是使用資源字典和數據綁定。以下是一個詳細的步驟和示例源代 ...
  • 描述(做一個簡單的記錄): 事件(event)的本質是一個委托;(聲明一個事件: public event TestDelegate eventTest;) 委托(delegate)可以理解為一個符合某種簽名的方法類型;比如:TestDelegate委托的返回數據類型為string,參數為 int和 ...
  • 1、AOT適合場景 Aot適合工具類型的項目使用,優點禁止反編 ,第一次啟動快,業務型項目或者反射多的項目不適合用AOT AOT更新記錄: 實實在在經過實踐的AOT ORM 5.1.4.117 +支持AOT 5.1.4.123 +支持CodeFirst和非同步方法 5.1.4.129-preview1 ...
  • 總說周知,UWP 是運行在沙盒裡面的,所有許可權都有嚴格限制,和沙盒外交互也需要特殊的通道,所以從根本杜絕了 UWP 毒瘤的存在。但是實際上 UWP 只是一個應用模型,本身是沒有什麼許可權管理的,許可權管理全靠 App Container 沙盒控制,如果我們脫離了這個沙盒,UWP 就會放飛自我了。那麼有沒... ...
  • 目錄條款17:讓介面容易被正確使用,不易被誤用(Make interfaces easy to use correctly and hard to use incorrectly)限制類型和值規定能做和不能做的事提供行為一致的介面條款19:設計class猶如設計type(Treat class de ...
  • title: 從零開始:Django項目的創建與配置指南 date: 2024/5/2 18:29:33 updated: 2024/5/2 18:29:33 categories: 後端開發 tags: Django WebDev Python ORM Security Deployment Op ...
  • 1、BOM對象 BOM:Broswer object model,即瀏覽器提供我們開發者在javascript用於操作瀏覽器的對象。 1.1、window對象 視窗方法 // BOM Browser object model 瀏覽器對象模型 // js中最大的一個對象.整個瀏覽器視窗出現的所有東西都 ...