java程序運行一段時間後內存爆滿,cpu使用率迅速增加(解決)

java程序在運行一段時間後,內存逐漸爆滿,隨後cpu使用率上升

上週遇到一個很奇葩的問題,現場反應,程序運行20分鐘以後cpu使用率在90%以上,拿到代碼無從下手,經過幾天的研究,最終找到原因並解決。

通過現場bug現象,初步分析,是由於程序佔用過多的系統資源,導致cpu使用率過高,懷疑是資源沒有合理釋放,或者程序在運行時出現死循環

一、通過windows自帶工具查看佔用內存的線程

https://blog.csdn.net/hexin373/article/details/8846919

我參照上面這篇文章,可以定位到具體問題位置,不得不說,這種方式也許可以解決幾乎所有由代碼引起的cpu使用率過高的問題,但我通過這種方式並沒有定位到程序的具體代碼,而是如下的位置:

 在計算機上,我的cpu使用率過高的線程被定位到了這幾個垃圾回收器上,進一步陷入迷惑,垃圾回收器不是java自己維護的嗎!!怎麼會有問題!!這也排除了cpu使用率過高是由於死循環造成的可能

二、cpu使用率高是由於GC線程

GC佔用cpu線程,那肯定是程序中資源佔用不釋放了,那就去排查代碼嘍,通過一系列鬼知道發生了什麼的操作(什麼開啓關閉部分功能,代碼logger打印查看...),最終定位到了一個定期任務上,然後就去看看哪的資源不釋放,也在網上看了下需要手動關閉的資源:

  1. io流需要手動關閉
  2. 獲取到的連接,不管是redis還是jdbc
  3. hibernate通過openSession創建的連接(getCurrentSession()綁定線程,在rollback或commit後自動歸還連接到池或關閉)

局部代碼排查後並沒有發現未釋放的資源...wtf....這怎麼辦!!!

三、問題描述

既然代碼沒問題,就從業務方面入手,下面我說下我的問題,希望能提供一些思路:

既然是定時任務模塊的問題,那就看定時任務,如下是定時任務的部分代碼:

 for (String runId : runIds) {
      tempObj = tempMap.get(runId);//從緩存中獲取運行中對象
      if(tempObj != null){
            // 異步執行普通節點線程任務
            DbThreadPool.getInstance().add( new DealRunTempTask(tempObj,dealDataService));
      }
  }

大概就是加載一個模版開啓一個線程,線程數已經被限定在了一定數量,那問題就是在處理每個線程的時間過長,導致多個任務疊加在一起,老線程沒運行完不釋放資源;新線程拿不到資源,無法運行。

線程運行時間過長是因爲每個線程中需要處理的數據越來越多,程序運行時間越長,疊加的待處理數據越多,一些數據在獲取失敗後,會判斷數據庫連接等是否可用,造成超時等待,這樣時間會更長,大概業務是這樣:

可以看出,處理失敗的數據只要不過期會不斷累加,造成處理時間越來越長,最終內存爆滿,cpu暴增。

 四、解決方法

爲業務設置合理超時時長,並優化數據處理失敗時的邏輯。使數據處理失敗的處理判斷用時更短,例如:同一類超時情況只判斷一次,失敗任務分批處理。

五、總結

我碰到的cpu使用率過高,是由於定時任務處理時間過長,每次定期任務間隔時間短,導致定期任務疊加執行,耗用大量系統資源。解決方法是:優化縮短每次定時任務執行時間。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章