線上機器Old GC問題排查記錄

2022-02-22 06:01:26 字數 1982 閱讀 8730

背景

上周五組隊去團建,結果路上收到線上機器發生old gc的簡訊告警。到達目的地後迅速連vpn執行了tomcat的重啟操作,問題得到解決。但是過了一會兒,又有兩台機器發生了old gc,此時判定是系統**問題,便執行了回滾操作,系統恢復正常。

問題分析

old gc發生時聯絡運維同學dump了機器記憶體,用visual vm開啟dump檔案如下圖

由圖可見,記憶體中的bigdecimal、consumedetails和date的例項每種都有1500w個,所占用的記憶體共達1700mb,推測可能是**中發生了死迴圈導致物件長時間不能被**。

發生old gc的jvm記憶體監控圖如下圖

可見在gc高峰期13:39分時,新生代的gc次數達到400多次,老年代的gc次數多達40次,此時jvm基本處於不可用狀態,所以造成系統拋了大量超時異常。

問題**

經過排查,造成此次問題的關鍵**如下

listresult = new arraylist();

while (begin.get(calendar.day_of_year) <= end.get(calendar.day_of_year)) else if (consumemap.containskey(key)) else

end.add(calendar.day_of_year,-1);

}

這裡是要構建起始時間和結束時間之間的資料,按天聚合。如果沒有資料就設定乙個預設值。

通常情況下並沒有問題,但是如果begin的值是2017-01-01 00:00:00,end的值是是2017-01-01 23:59:59,begin.get(calendar.day_of_year)值為1,end.get(calendar.day_of_year)值為1,此時可以進入迴圈體。

當執行完end.add(calendar.day_of_year,-1)後,end的值變為2016-12-31 23:59:59,此時問題就來了,end.get(calendar.day_of_year)的值為365。

然後迴圈繼續,等到end的值為2016-01-01 23:59:59時,執行完end.add(calendar.day_of_year,-1)後,end的值變為2015-12-31 23:59:59。然後迴圈會一直進行下去,由於多數時間是沒有資料的,所以絕大多數迴圈會進入else部分。

else**塊裡會執行new consumedetails()和new bigdecimal()操作,由於這裡new出來的物件一直被result引用導致不能被**,經過多次gc後可能進入到老年代中。當老年代記憶體不足時就會發生old gc導致jvm失去響應。

總結此次事故主要由**不規範引起。使用while迴圈時,一定要非常清楚while迴圈的邊界在**。相比之下,更推薦先確定迴圈邊界,然後使用for迴圈。

曾經看過一本書上提到過類似下面的**的優化。

for(int i = 0; i < list.length; i++)
推薦寫法為

int length = list.length;

for(int i = 0; i < length; i++)

將迴圈邊界提前計算好存在length變數裡,可以避免每次迴圈都要去計算迴圈邊界。

線上問題排查

問題排查方 長期改進建議 由於業務應用 bug 本身或引入第三方庫 環境原因 硬體問題等原因,線上服務出現故障 問題幾乎不可避免。例如,常見的現象包括請求超時 使用者明顯感受到系統發生卡頓等等。作為乙個合格的研發人員 技術人員 不僅要能寫得一手好 掌握如何排查問題技巧也是研發人高階必須掌握的實戰技能...

線上ZK問題排查

問題描述 測試環境zk集群的三個節點中zk1狀態雖然是follower,啟動也能正常啟動 通過telnet也能telnet 2181埠 無法通過zk客戶端去連線2181埠,狀態一致是connecting 檢視zk集群所有節點狀態 data zookeeper new 1 bin zkserver.s...

技術 排查線上問題

產品交付後對於專案管理人員往往就是工作接近尾聲,但是對於一線開發和運維的小夥伴,那是維護工作的起點.線上問題對於程式設計師來說其實就是生產環境中遇到的問題總稱,優先順序對於開發同學和運維同學來說是最高端別.當一位工程師遇到線上問題,一般都會放下手頭的工作,優先全力解決線上問題.往往系統運維工作和線上...