Java GC問題排查的一些個人總結和問題復盤

個人博客

Java GC問題排查的一些個人總結和問題復盤 | iwts’s blog

是否存在GC問題判斷指標

有的比較明顯,比如發布上線后內存直接就起飛了,這種也是比較好排查的,也是最多的。如果單純從優化角度,看當前應用是否需要優化,有一些指標判斷:

  1. 延遲(Latency):也可以理解為最大停頓時間,即STW最長時間。
  2. 吞吐量(Throughput):例如系統運行了 100 min,GC 耗時 1 min,則系統吞吐量為 99%。

兩者是結合的,延遲越低越好,而吞吐量一般有各個業務指標,例如TP999,即99.9%的吞吐量,TP9999則是99.99%。

一般來說尤其是技術還不錯的大廠,除了發布是很難碰到相關問題的。。尤其是阿里這種比較卷比的,早就優化甚至為了ppt而優化給整完了,確實沒啥優化空間。

包括JVM在內,這種東西能不碰就不碰,真的性價比不高。不過日常代碼還是要注意,比如內存泄露這種代碼就別寫出來了。

這里主要還是聊聊如果真碰到了問題,或者性能不是很行,想要分析是否是GC的話,有一些我個人工作的經驗總結以及之前看到的解決方案的分析。

通用排查流程

GC日志分析

有一些網站,dump下來GC日志后上傳上去,各種可視化信息。例如:Universal JVM GC analyzer - Java Garbage collection log analysis made easy

分析JVM內存配置

jmap -heap {pid}

先看看JVM啟動參數&各代內存分配情況:

先看看分代參數不合理是否會影響本次GC問題。

堆內存對象大小分析

查看存活對象中的實例數量&具體占用內存大小:

jmap -histo 7276 | head -n20

后面的可以忽略,只看前面一部分就ok。

或者直接>重定向寫到log中,慢慢看。

主要看是否有哪個對象占用量非常的大,遠超過其他對象,如果有,那說明該對象的生成可能是不合理的。

堆文件dump分析

確定有比較異常的對象,才考慮dump下來看。

JProfile之類的比較高端,能直接遠程監控VM,但是需要線上配置。自己負責的業務能線上直接操作,或者基建比較nb已經有相關內部工具,那么可以代替dump。

dump命令:

jmap -dump:format=b,file={文件名} {pid}

然后利用可視化工具裝載,例如JProfile、JVisualVM。JProfile要破解,JDK自帶JVisualVM。

可以具體分析對象到底是哪些實例。

JVisualVM

JDK自帶可視化分析工具。

路徑JAVA_HOME/bin/jvisualvm.exe

Idea可以下載插件:VisualVM,配置后可以直接拉起比較方便。

本質上是監控,但是一般公司基建都不錯,不在乎這種。主要還是快,直接打開exe文件,省的再去JDK下找。

類加載情況

JVM啟動參數,增加內容:

-verbose:class # 查看類加載情況
-verbose:gc # 查看虛擬機中內存回收情況
-verbose:jni # 查看本地方法調用的情況

一般是class,啟動后java -verbose:class,可以看到當前程序的加載情況。

代碼分析

總歸是要回歸代碼的。尤其是上線后導致的問題,更容易排查出問題的到底是在哪,必然是因為上線后更新的代碼導致的。

不管怎么調優,最終還是要回歸代碼的。

頻繁Full GC

內存空間角度

  1. 先看JVM內存配置,是不是老年代太小,實在沒啥空間,頻繁觸發閾值開啟Full GC。
  2. 是否有內存泄漏,老年代增長速度非常快,回收后跟沒回收一樣,可能是內存泄漏。

大對象

例如單SQL未分頁,同時刻大對象裝載進入內存,此時由于超過了Eden區大小,會直接裝載進老年代,從而導致Full GC頻繁。

這樣在觀察heap的時候就能看出來,是否會出現某些對象實例少,占用空間大。

內存泄漏

比較經典了,每次Full GC只能回收一點,機器重啟后解決問題。標準的內存泄漏。主要看看IO之類的是不是哪里沒有close。

Minor GC角度

會不會是Minor GC頻率太高導致的。高頻次的Minor GC會導致大量的對象年齡增加以進入老年代。

晉升代數設置不合理-調整代數設置

如果Minor GC的頻率確實這么高,那么考慮是不是晉升代數設置的太低導致的。例如對象A生命周期60秒,Minor GC 10s一次,而晉升代數設置的是3。

那么A對象30s就會晉升一次,導致大量A對象進入老年代。

如果設置成晉升代數為7,那么在60s后,A對象生命周期結束,年齡為6,Minor GC直接清除,不再丟入老年代。

OOM

一般伴隨著Full GC的問題。場景不多,給一些比較常見的。

內存泄漏

同上,一般先是大量Full GC,老年代遲早要被完全填滿的,填滿后就是OOM。

ThreadLocal內存泄漏

比較經典,可以參考:詳解Java ThreadLocal

new大量新類

比較少見,仍然是先高頻率Full GC,最終填滿導致OOM

內存空間不合理

可能有很多,哪個空間都有可能OOM,此時異常一般也說明了是哪個區OOM了,一般很少見,真碰見一般就是非常低的情況。比如之前CRM不知道誰給設置的,Heap 100MB還是多少,Full GC的🐎都不認識,但是最后只有少量OOM。(大對象還是少)

堆外內存泄漏

感覺還是相當少了,之前吃交易P0的瓜,好像是這個問題。

首先回顧以下堆外內存,JVM分為堆和非堆,堆之外的,包括本地內存、棧等。一般JVM控制的內存大小是固定的,反映在監控上,在內存占用這里基本就是一條直線。

蘑菇街這邊基本就是75%這樣。

出現時的表現:

  1. 內存使用率不斷上升,甚至開始使用 SWAP 內存。
  2. GC 時間飆升。
  3. 線程被 Block。
  4. 通過 top 命令發現 Java 進程的 RES 甚至超過了 -Xmx 的大小。

例如:

結合上次交易的P0事故。一般發生堆外內存泄漏,都跟我們自己的代碼關系不太大。因為大量調用棧這種情況一般不出現,出現也是OOM,不會產生對機器內存大量讀寫的場景。那么有那種情況下會對內存大量讀寫?native方法。

那么有個最重要的點:IO通信框架一般是會大量操作本地內存的。

大家都是RPC,內部可能是Netty或者NIO框架,所以RPC框架是很有可能導致堆外內存泄漏的,通信過程中大量使用JNI方法調用本地線程,這樣指向了一個非常常見的問題:瞬時大量長時間請求。

比如接口日常請求100QPS,并發量也就10這樣,如果跑任務之類的,瞬間QPS達到5000,直接堆外內存就打滿了。

交易上次P0基本確定是這個問題。緩存擊穿之后大量請求讀寫接口,Tesla接口QPS達到了萬級,瞬間OOM機器開始掛。后續多次沒起來也是,機器剛啟動,Tesla線程拉起后直接OOM,繼續掛。

給個云總的blog:netty oom

這里也有個小知識點:堆外內存是不由JVM管理的,所以大量占用的時候,GC不會被觸發,同時JVM基本上也不限制本地內存大小。所以這塊很難防止。

Metaspace OOM

具體分析可以看一下這個 深入JVM元空間以及彈性伸縮機制

已經聊了一些Metaspace OOM問題了。本質上就是一般設定都是固定大小不擴容,然后大量新class被load進去,導致OOM。

所以除了RPC框架,也有可能是跟IO相關的其他框架導致的,例如fastjson就有可能會在序列化中利用ASM技術執行字節碼增強,產生大量的class對象。

這種情況本質上是類的內部方法建立的對象,存儲在Metaspace的klass空間中。那么此時大量請求時就會不斷創建,由于沒有GC導致OOM。

此時兩種方案:

  1. 設置Metaspace大小。默認情況下一般是非常大的,所以沒有上限,設置一個大小觸發卸載,可能會緩解這個問題。
  2. 本質上還是要看代碼。一般這種情況還是代碼有問題,能頻繁不在堆上創建對象,說明該方法一般可以通過static固定到運行時常量池中,全部類只存一份。

Minor GC時間長

晉升代數不合理

和上面的Full GC恰好相反,有可能是晉升代數設置的太高了。那么此時的表現可能是:From區和To區Minor GC一次只能GC掉很少的數據,導致剩余空間小,每次Minor GC后,From/To區進行復制,這個時間花費太長了。

存在大量長生命周期對象

這個跟晉升代數類似,都是高齡對象堆滿From/To區,但是不同點在于:此時晉升代數的設置是比較合理的。

那么這種情況就是高齡對象太多,導致的。還是要從heap 文件中找思路。很有可能是某個集合里面對象太多。比如一個list里面存幾十萬的對象。

那么主要就是先去找大對象,找到大對象后分析大對象的屬性,看為什么能這么大。

頻繁Minor GC

Eden擴容

一般來說可以對Eden區進行擴容來減少Minor GC次數,也就是說,增加了Minor GC的時間間隔,一次GC可以回收更多的對象。這里需要聊下新生代的GC算法:

  1. 新生代掃描。
  2. 復制Eden存活對象到Survivor。

這兩步都存在時間消耗,但是復制要比掃描需要的時間多很多。

所以,對于Eden分區的擴容需要根據實際對象生命周期來計算,有這樣的場景:

  1. 對象生命周期<擴容后Minor GC時間間隔。

    a. 此時對象只會被掃描,掃描后標記清除,不進行復制。

  2. 對象生命周期>=擴容后Minor GC時間間隔。

    a. 此時對象跟擴容前一樣,先掃描后復制。

再結合上面掃描和復制的性能損耗:如果能保證擴容后Minor GC能將原來不能回收的對象給回收掉,那么收益是很大的。

反之,如果對象生命周期長,那么由于Eden區的擴大,會導致掃描時間變長,所以Minor GC時間也會增加。所以,有可能實際工作時間會降低。

所以Eden擴容并不是完美解決方案,依然要先分析對象存活時間之類的參數,然后再考慮擴容。

簡而言之:如果對象平均代數低,那么擴容是有效的。

高峰期CMS Full GC時間突刺

CMS Full GC只有在Remark階段會進行長時間STW,初始標記只是遍歷GC Roots,STW很快。例如下面,Remark階段STW時間為1.39s:

這里可能是Full GC慢的一個很重要的問題:跨代引用。具體可以看下:JVM CMS 在Full GC時針對跨代引用的優化

那么在高峰期可以看,在Full GC發生Remark的時候,新生代對象數量是否有很多,所以會出現這種突刺類型的問題:

  1. 如果Full GC前已經Minor GC一次。

    a. 那么跨代引用掃描很少數據,Full GC快。

  2. 如果Full GC時恰好新生代很滿,例如75%。

    a. 那么跨代引用掃描大量數據,Full GC慢。

為了解決這個問題,CMS本身就有一些優化,上面link的文章已經聊到了。

那么同樣是上圖,發現CMS-concurrent-abortable-preclean階段執行時間5.35s,超過了默認5s的等待,所以可以認為Remark時是沒有進行Minor GC的。

這種情況下可以調高CMSMaxAbortablePrecleanTime(不推薦),或者設置CMSMaxAbortablePrecleanTime(推薦),在Remark前強制執行一次Minor GC。

啟動時大量GC后趨于正常(空間震蕩)

表現還是比較經典的:

  1. 啟動后頻繁GC。
  2. 每次GC時占用內存空間都很小,但是每次GC后都會增加。

JVM配置中,各個空間一般都是配置兩個:一個正常,一個最大。而在JVM初始化的時候,是按正常的分配的。

所以說這種問題就是初始空間配置小了,很快就需要執行GC,調大即可。

顯式調用

When you have eliminated the impossibles, whatever remains, however improbable, must be the truth.

如果確實排查不出來問題,全局搜一下

System.gc()

說不定確實有那個sb上傳了測試代碼,真的在調用。。。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/18091.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/18091.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/18091.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

探索旅行的優惠之選,千益暢行旅游卡讓旅程更省心省力!

在旅行的道路上&#xff0c;一張旅游卡往往能為您帶來意想不到的便利與優惠。那么&#xff0c;對于千益暢行旅游卡&#xff0c;您是否好奇如何輕松擁有它呢&#xff1f; 首先&#xff0c;千益暢行旅游卡作為旅行者的貼心伴侶&#xff0c;為您提供了多樣化的獲取渠道。您可以通…

Unity實現首行縮進兩個字符

效果 在Unity中如果想實現首行縮進兩個字符&#xff0c;你會發現按空格是沒法實現的。 實現原理&#xff1a;用空白的透明的字替代原來的位置。 代碼&#xff1a; <color#FFFFFF00>XXX</color> 趕緊去試試吧&#xff01;

備戰秋招—模擬版圖面試題來了

隨著暑期的腳步逐漸臨近&#xff0c;電子工程和集成電路設計領域的畢業生們&#xff0c;也將迎來了另一個求職的黃金期——秋招。我們總說機會是留給有準備的人。對于有志于投身于模擬版圖設計的學子們來說&#xff0c;為了在眾多求職者中脫穎而出&#xff0c;充分備戰模擬版圖…

C# 工商銀行缺少infosecapiLib.infosec

搜索Tlbimp.exe 這里使用4.8.1下的處理&#xff0c;以管理員身份打開powershell cd "C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8.1 Tools".\TlbImp.exe "G:\CSharp\icbc-api-sdk-cop-c#\sdk-cop\sdk-cop\dll\infosecapi.dll" …

PCIe協議之-DLLP詳解

?前言&#xff1a; &#x1f31f;數據鏈路層的功能 數據鏈路層將從物理層中獲得報文&#xff0c; 并將其傳遞給事務層&#xff1b; 同時接收事務層的報文&#xff0c; 并將其轉發到物理層; 核心的功能有以下三點 1.保證TLP在 PCIe 鏈路中的正確傳遞; 2.數據鏈路層使用了容錯…

頁面導出PDF,非可視區域如何解決

const exportToPDF () > {const element document.getElementById(chart-container);if (!element) return;const originalScrollHeight element.scrollHeight;// 臨時解除滾動條限制&#xff0c;確保所有內容都可見element.style.height ${originalScrollHeight}px;// …

殺死那個進程

一、場景 eclipse在啟動tomcat時&#xff0c;出現端口被占用的情況。我尋思著“任務管理器”沒出現相應程序在跑啊。 1.1問題&#xff1a;端口和進程的關系 端口和進程之間存在著一種關系&#xff0c;端口是一個邏輯概念&#xff0c;它用于標識網絡通信中的一個終點&#xff0…

SEC突發:以太坊ETF大概率獲批

美國證監會大概率批準以太坊現貨ETF。 5月20日&#xff0c;據外媒CoinDesk報道&#xff0c;知情人士透露&#xff0c;美國SEC周一要求證券交易所更新以太坊現貨ETF的19b-4備案文件。19b-4備案文件是一種表格&#xff0c;用于向SEC通報允許基金在交易所交易的規則變更。 三位消息…

利用cherry pick巧妙地將某次提交單獨合并到其他分支

0. 引言 最近在進行系統的多版本并行開發&#xff0c;涉及一些共有基礎功能提交時就遇到了麻煩&#xff0c;一份代碼需要向多個版本分支進行同步&#xff0c;以保證多版本都能有更新該基礎功能。 多次對比提交的方式顯然會帶來巨大的工作量。但實際上我們可以通過git的cherry…

「Python Socket超能力:網絡世界的隱形斗篷!」

Hi&#xff0c;我是阿佑&#xff0c;今天將帶領大家揭開Python Socket編程的神秘面紗&#xff0c;賦予我們的網絡應用隱形斗篷般的超能力&#xff01; 深入探討Socket編程的革命性力量&#xff0c;教你如何用Python的Socket模塊來構建強大的網絡應用。從簡單的HTTP服務器到復雜…

MagicLens:新一代圖像搜索技術和產品形態

MagicLens&#xff1a;Self-Supervised Image Retrieval with Open-Ended Instructions MagicLens: 自監督圖像檢索與開放式指令 作者&#xff1a;Kai Zhang&#xff0c; Yi Luan&#xff0c; Hexiang Hu&#xff0c; Kenton Lee&#xff0c; Siyuan Qiao&#xff0c; Wenhu …

Selenium操作瀏覽器Cookie(增/刪/查看cookie)

天行健&#xff0c;君子以自強不息&#xff1b;地勢坤&#xff0c;君子以厚德載物。 每個人都有惰性&#xff0c;但不斷學習是好好生活的根本&#xff0c;共勉&#xff01; 文章均為學習整理筆記&#xff0c;分享記錄為主&#xff0c;如有錯誤請指正&#xff0c;共同學習進步。…

更新.gitmodules的子模塊倉庫地址,但是沒有生效,需要運行命令

當你更新了 .gitmodules 文件中的子模塊倉庫地址后&#xff0c;為了使這些更改生效并同步到實際的子模塊目錄&#xff0c;你需要執行以下步驟&#xff1a; 同步.gitmodules的更改&#xff1a; 使用 git submodule sync 命令來同步.gitmodules文件中的URL修改到你的本地配置。執…

在VS Code中進行Java的單元測試

在VS Code中可以使用 Test Runner for Java擴展進行Java的測試執行和調試。 Test Runner for Java的功能 Test Runner for Java 結合 Language Support for Java by Red Hat 和 Debugger for Java這兩個插件提供如下功能&#xff1a; 運行測試&#xff1a; Test Runner for …

Flutter 中的 Flex 小部件:全面指南

Flutter 中的 Flex 小部件&#xff1a;全面指南 Flutter 的布局系統非常靈活&#xff0c;允許開發者以聲明式的方式構建復雜的用戶界面。Flex 是 Flutter 中用于創建靈活布局的核心小部件之一&#xff0c;它提供了水平和垂直的線性布局能力。本文將詳細介紹 Flex 小部件的使用…

QT學習(20):QStyle和自定義樣式

QStyle 樣式&#xff08;繼承自QStyle類&#xff09;代表控件的繪制并封裝GUI的外觀。QStyle是一個封裝了GUI外觀的抽象基類。Qt使用QStyle去執行幾乎所有的內置控件的繪制&#xff0c;確保控件外觀和原生控件風格風格相同。 class Q_WIDGETS_EXPORT QStyle : public QObject{…

【OpenCV】圖像通道合并與分離,ROI

介紹可以實現圖像通道合并與分離的API&#xff0c;這只是一種方式&#xff0c;后續還會介紹其他的合并與分離方法&#xff0c;以及ROI區域截取的方法。相關API&#xff1a; split() merge() Mat對象() 代碼&#xff1a; #include "iostream" #include "ope…

Hive的小文件處理

針對ORC存儲格式的小文件 --orc合并小文件的特定語法,使用concatenate(連接、使連續)關鍵字 --非分區表 alter table table_name concatenate;--分區表 alter table table_name partition(dtxxx) concatenate;針對TEXTFILE存儲格式的小文件 --將這些小文件進行合并,這里使用d…

MySQL進階之(九)數據庫的設計規范

九、數據庫的設計規范 9.1 范式的概念9.1.1 范式概述9.1.2 鍵和相關屬性 9.2 常見的范式9.2.1 第一范式9.2.2 第二范式9.2.3 第三范式9.2.4 第四范式9.2.5 第五范式&#xff08;域鍵范式&#xff09; 9.3 反范式化9.3.1 概述9.3.2 舉例9.3.3 反范式化新問題9.3.4 通用場景 9.4 …

18 - grace數據處理 - 補充 - 地下水儲量計算過程分解 - 地表水儲量變化Glads水文數據處理

18 - grace數據處理 - 補充 - 地下水儲量計算過程分解 - 地表水儲量變化 0 引言1 Grace陸地水儲量過程整合0 引言 由水量平衡方程可以將地下水儲量的計算過程分解為3個部分,第一部分計算陸地水儲量變化、第二部分計算地表水儲量變化、第三部分計算地下水儲量變化。本篇簡單介紹…