問題解決:遇到tomcat的假死問題,如何排查問題

問題場景

線上,有時候會遇到一種這樣的情況:tomcat沒有奔潰退出,輸出日誌也沒有異常,但是界面訪問就一直卡着。假如遇到這種情況,沒錯,你遇到了tomcat假死問題了。那麼,該怎麼排查這個問題呢?這個就是本文的重點了。

在這裏插入圖片描述

問題環境

軟件 版本
tomcat 7.0
JDK 1.6
Centos 6

問題原因

在這裏插入圖片描述

tomcat假死的原因有多種,這裏羅列博主遇到的幾種情況:

  1. HashMap死鎖
  2. 內存泄露
  3. CLOSE_WAIT過多

排查過程

遇到這種tomcat假死的情況,先不着急重啓應用,先排查一下。

在這裏插入圖片描述

以下是博主之前操作的過程:

查看tomcat的gc情況

使用以下命令拿到對應的gc情況,命令如下:

jmap -heap pid >> jvm_memory.log

這個命令,主要是打印堆摘要,其中包括使用的GC算法、堆配置和生成堆使用情況等。博主生成的文件當中,關於GC部分如下:

using thread-local object allocation.
Parallel GC with 16 thread(s)

Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 3221225472 (3072.0MB)
   NewSize          = 2686976 (2.5625MB)
   MaxNewSize       = -65536 (-0.0625MB)
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2
   SurvivorRatio    = 8
   PermSize         = 134217728 (128.0MB)
   MaxPermSize      = 268435456 (256.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 935133184 (891.8125MB)
   used     = 171217136 (163.28538513183594MB)
   free     = 763916048 (728.5271148681641MB)
   18.309385115350587% used
From Space:
   capacity = 68878336 (65.6875MB)
   used     = 41388552 (39.47119903564453MB)
   free     = 27489784 (26.21630096435547MB)
   60.089361043797574% used
To Space:
   capacity = 67174400 (64.0625MB)
   used     = 0 (0.0MB)
   free     = 67174400 (64.0625MB)
   0.0% used
PS Old Generation
   capacity = 2147483648 (2048.0MB)
   used     = 2142115640 (2042.8806686401367MB)
   free     = 5368008 (5.119331359863281MB)
   99.75003264844418% used
PS Perm Generation
   capacity = 161087488 (153.625MB)
   used     = 159574696 (152.18228912353516MB)
   free     = 1512792 (1.4427108764648438MB)
   99.06088795673566% used

從輸出結果,我們可以看到目前是在進行GC操作。

在這裏插入圖片描述

另外,我們也可以使用命令jstat來查看,命令如下:

jstat  -gcutil pid 250 7

說明:
pid: 進程號
250:間隔時間,單位爲毫秒
7: 輸出次數,這裏指輸出7次

因爲當時排查的時候,沒有使用這個命令。等問題解決了,才發現有這個命令,沒辦法復現當時的情況。

在這裏插入圖片描述

爲了給大家講解一下,就運行此命令,展示當前應用的情況:

S0 S1 E O P YGC YGCT FGC FGCT GCT
0.00 0.00 82.15 84.94 99.84 50388 5413.400 2870 18355.985 23769.385
0.00 0.00 86.08 84.94 99.84 50388 5413.400 2870 18355.985 23769.385
0.00 0.00 90.86 84.94 99.84 50388 5413.400 2870 18355.985 23769.385
0.00 0.00 93.88 84.94 99.84 50388 5413.400 2870 18355.985 23769.385
0.00 0.00 97.54 84.94 99.84 50388 5413.400 2870 18355.985 23769.385
23.60 0.00 5.96 84.94 99.84 50389 5413.476 2870 18355.985 23769.461
23.60 0.00 21.19 84.94 99.84 50389 5413.476 2870 18355.985 23769.461

說明:下面是各個選項的含義

說明
S0 Heap上的 Survivor space 0 段已使用空間的百分比
S1 Heap上的 Survivor space 1 段已使用空間的百分比
E Heap上的 Eden space 段已使用空間的百分比
O Heap上的 Old space 段已使用空間的百分比
P Perm space 已使用空間的百分比
YGC 從程序啓動到採樣時發生Young GC的次數
YGCT Young GC所用的時間(單位秒)
FGC 從程序啓動到採樣時發生Full GC的次數
FGCT 完整的垃圾收集時間(單位秒)
GCT 總垃圾收集時間(單位秒)

排查內存泄露問題

一般應用運行了很久,突然纔出現問題,可能是有幾個原因:

  1. 外部原因的變化,如遠程服務異常;
  2. 內部原因的變化,如升級代碼存在問題等。

一般針對內存泄露,除了開發代碼的時候,就進行規範之後,等運行之後,要查看該問題,特別是線上環境的話,最好將完整的JVM堆棧信息dump下來。這個時候可以使用以下命令:

jmap -dump,format=b,file=heap-dump.bin <pid>

這個命令的作用爲:將hprof二進制格式的Java堆轉儲到filenamelive子選項是可選的。如果指定,則只轉儲堆中的活動對象。要瀏覽堆轉儲,可以使用jhat(Java堆分析工具)讀取生成的文件。

在這裏插入圖片描述

轉換的文件比較大,一般有幾個GB。所以要從現場環境拿下來,肯定得先進行壓縮,然後再下載。這裏我推薦使用JProfiler來進行JVM分析。關於JProfiler,請自行搜索使用。

在這裏插入圖片描述

使用JProfiler打開head-dump.bin文件,就可以看到一個很明顯的東西,截圖如下:

在這裏插入圖片描述

在上圖可以知道,在Classes頁面,有一個類路徑下面,生成了很多對象。我們點擊Biggest Objects,截圖如下:

在這裏插入圖片描述

從上圖可以知道,有一個對象非常大,佔據了1487MB。而從第一步拿到的堆棧信息,整個堆棧最大是3072.0MB。所以,初步可以判斷是因爲這個東西,導致了頻繁的GC,進行導致tomcat假死。

在這裏插入圖片描述

通過諮詢項目運維成員,說該對象屬於以前的插件-聽雲,屬於端對端監控的插件。該插件之前要下線的,但是沒有執行成功。所以該插件目前還運行着。而最近聽雲服務器正式下線,所以導致tomcat連接聽雲出現問題。暫時鎖定該問題,所以先安排運維人員移除該插件,並重啓tomcat。其中也遇到一個問題,並形成博客《問題解決:啓動tomcat,日誌輸出:java.lang.ClassNotFoundException: com.tingyun.api.agent.TingYunApiImpl》,有興趣的小夥伴可以看看。

重啓之後,目前運行將近一週了,還未發現tomcat假死現象。所以,可以肯定,這個插件是造成近期應用假死的元兇。

在這裏插入圖片描述

這個原因是屬於近期頻繁假死的真兇,但是運維人員反映,該應用在之前是會出現這種tomcat假死的情況的。只是頻率不會像現在一天兩次的情況,一般是數週會發生一次。所以,還是得繼續排查。

在這裏插入圖片描述

HashMap死鎖

在應用運行一段時間之後,使用阿里巴巴開源的arthas來進行問題排查。這個是排查Java問題的利器。關於arthas的使用,請自行登錄官網查看。

在這裏插入圖片描述

登錄機器,使用arthas連接應用,查看當前的線程情況,發現有意思的一幕,截圖如下:

在這裏插入圖片描述

有兩個線程一直掛着,沒有退出過。通過命令thread查看該線程具體情況,截圖如下:

在這裏插入圖片描述

如果曾經遇到過這個問題的話,應該可以知道,這個就是所謂的HashMap死鎖問題。這個需要去查看具體的代碼,查看爲什麼會發生死鎖的問題。

在這裏插入圖片描述

從這個可以得到,假如這個HashMap死鎖發生的頻率不高,但是隨着時間的推移,這種情況不斷髮生,就會將全部的連接給佔據了,導致了tomcat沒可用連接進行響應,就會導致tomcat假死的情況發生。這個就是爲什麼之前應用發生死鎖的頻率比較低,要數週纔可能會發生一次。

在這裏插入圖片描述

CLOSE_WAIT過多情況

在實際場景,我們也會發現一種情況,tomcat假死的時候,使用以下命令獲取TCP連接情況:

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

結果如下(命令可能和截圖不同,因爲這個是進行篩選並定時執行得到的):

在這裏插入圖片描述

可以看到,目前存在很多的CLOSE_WAIT。這個是因爲服務端未及時釋放資源。關於這個問題,除了可以修改Linux配置,我們也可以直接修改tomcat的配置文件,禁用wait,讓連接在返回後立馬關閉,成爲一個可用連接。新增選項:

<Connectorport=“11011” protocol=“org.apache.coyote.http11.Http11NioProtocol” connectionTimeout=“20000” maxThreads=“1000” URIEncoding=“UTF-8” keepAliveTimeout=“0” />

其中的keepAliveTimeout="0"是解決這個問題的關鍵。這個參數在其中一個應用進行試驗,原本經常有CLOSE_WAIT,新增之後,基本沒有,而且也沒有報錯。

在這裏插入圖片描述

總結

通過實際的現場場景,展示了整個排查的過程,中間涉及jmapjstackjstatarthasjprofiler等命令及軟件。熟練地使用這些命令,可以得到很多有用的信息,並加以解決。

在這裏插入圖片描述

參考鏈接

jmap官方說明文檔
jstack官方說明文檔
jstat官方說明文檔
arthas官方文檔

參考圖

JVM結構

在這裏插入圖片描述

TCP的三次握手和四次揮手

在這裏插入圖片描述

隨緣求贊

如果我的文章對大家產生了幫忙,可以在文章底部點個贊或者收藏;
如果有好的討論,可以留言;
如果想繼續查看我以後的文章,可以點擊關注
可以掃描以下二維碼,關注我的公衆號:楓夜之求索閣,查看我最新的分享!

在這裏插入圖片描述

拜拜

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