最近服務器發現tomcat的應用會偶爾出現沒法訪問的狀況。通過一段時間的觀察最近又發現有臺tomcat的應用出現了沒法訪問狀況。
簡單描述下該臺tomcat當時具體的表現:客戶端請求沒有響應,查看服務器端tomcat的進程是存活的,查看業務日誌的時候發現日誌中止沒有任何最新的訪問日誌。
連tomcat下面的catalina.log也沒有任何訪問記錄,基本判定該臺tomcat已不能提供服務。
根據前面我描述的假死現象,我最早想到的是網絡是否出現了問題,是否是有什麼丟包嚴重的狀況,因而我開始從請求的數據流程開始分析,因爲咱們業務的架構採用的是nginx+tomcat的集羣配置,一個請求上來的流向能夠用下圖來簡單的描述一下:linux
更改nginx的配置,讓該臺nginx請求只轉到本機器的出現問題的tomcat應用上面,在access.log裏看是否有網絡請求,結果能夠查看到當前全部的網絡請求,也就是說能夠排除是網絡的問題。
分析業務配置的tomcat訪問日誌xxxx.log上是否有日誌訪問記錄,通過查詢該臺tomcat應用日誌徹底沒有任何訪問記錄,因爲咱們的部署是本機的nginx轉到本機的tomcat應用,因此能夠排除不是網絡問題。
到此基本能夠判定網絡沒有問題,tomcat 自己出現了假死的狀況。在tomcat的日誌裏有報過OutOfMemoryError的異常,因此能夠確定tomcat假死的緣由是OOM
在咱們學習Java的時候就知道它最爲方便的地方就是咱們不須要管理內存的分配和釋放,一切由JVM本身來進行處理,當Java對象再也不被應用時,等到堆內存不夠用時JVM會進行GC處理,
清除這些對象佔用的堆內存空間,可是若是對象一直被應用,那麼JVM是沒法對其進行GC處理的,那麼咱們建立新的對象時,JVM就沒有辦法從堆中獲取足夠的內存分配給此對象,這時就會致使OOM。
咱們出現OOM緣由,通常都是由於咱們不斷的往容器裏存放對象,然而容器沒有相應的大小限制或清除機制,這樣就容易致使OOM。
當咱們的應用服務器佔用了過多內存的時候,咱們怎麼樣才能快速的定位問題呢?要想快速定位問題,首先咱們必需獲取服務器JVM某時刻的內存快照。
Jdk裏面提供了不少相應的命令好比:jstack,jstat,jmap,jps等等. 在出現問題後咱們應該快速保留現場。
能夠觀察到jvm中當前全部線程的運行狀況和線程當前狀態.
sudo jstack -F 進程ID
輸出內容以下:
從上面的圖咱們能夠看到tomcat進程裏面沒有死鎖的狀況,並且每一個線程都處理等待的狀態。這個時候咱們能夠telnet命令連上tomcat的端口查看tomcat進程是否有任務迴應。
這時發現tomcat沒有任何迴應能夠證實tomcat應用已沒有響應處理假死狀態。
在thread dump中,要留意下面幾種狀態
死鎖,Deadlock(重點關注)
等待資源,Waiting on condition(重點關注)
• 等待獲取監視器,Waiting on monitor entry(重點關注)
阻塞,Blocked(重點關注)
• 執行中,Runnable
• 暫停,Suspended
• 對象等待中,Object.wait() 或 TIMED_WAITING
• 中止,Parked
這是jdk命令中比較重要,也是至關實用的一個命令,能夠觀察到classloader,compiler,gc相關信息
具體參數以下:
-class:統計class loader行爲信息
-compile:統計編譯行爲信息
-gc:統計jdk gc時heap信息
-gccapacity:統計不一樣的generations(包括新生區,老年區,permanent區)相應的heap容量狀況
-gccause:統計gc的狀況,(同-gcutil)和引發gc的事件
-gcnew:統計gc時,新生代的狀況
-gcnewcapacity:統計gc時,新生代heap容量
-gcold:統計gc時,老年區的狀況
-gcoldcapacity:統計gc時,老年區heap容量
-gcpermcapacity:統計gc時,permanent區heap容量
-gcutil:統計gc時,heap狀況
-printcompilation:不知道幹什麼的,一直沒用過。
通常比較經常使用的幾個參數是:
sudo jstat -class 2083 1000 10 (每隔1秒監控一次,一共作10次)
查看當時的head狀況
sudo jstat -gcutil 20683 2000
注:該圖不是出錯截取
出現時候截取的數據是gc已經徹底沒有處理了,由於沒有加上full gc的日誌因此不肯定JVMGC 時間過長,致使應用暫停.
Jdk自帶的jmap能夠獲取內在某一時刻的快照 命令:jmap -dump:format=b,file=heap.bin <pid> file:保存路徑及文件名 pid:進程編號(windows經過任務管理器查看,linux經過ps aux查看) dump文件能夠經過MemoryAnalyzer分析查看,網址:http://www.eclipse.org/mat/,能夠查看dump時對象數量,內存佔用,線程狀況等。 從上面的圖能夠看得出來對象沒有內存溢出。 從上圖咱們能夠明確的看出此項目的HashMap內存使用率比較高,由於咱們的系統都是返回Map的數據結構因此佔用比較高的內存是正常狀況。
觀察運行中的jvm物理內存的佔用狀況。咱們也能夠用jmap命令
參數以下:
-heap:打印jvm heap的狀況
-histo:打印jvm heap的直方圖。其輸出信息包括類名,對象數量,對象佔用大小。
-histo:live :同上,可是隻答應存活對象的狀況
-permstat:打印permanent generation heap狀況
命令使用:
jmap -heap 2083
能夠觀察到New Generation(Eden Space,From Space,To Space),tenured generation,Perm Generation的內存使用狀況
輸出內容:
上圖爲tomcat應用出錯前JVM的配置信息,能夠明確的看到當時的信息:
MaxHeapSize堆內存大小爲:3500M
MaxNewSize新生代內存大小:512M
PermSize永久代內存大小:192M
NewRatio設置年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代)。設置爲2,則年輕代與年老代所佔比值爲1:2,年輕代佔整個堆棧的1/3
SurvivorRatio設置年輕代中Eden區與Survivor區的大小比值。設置爲8,則兩個Survivor區與一個Eden區的比值爲2:8,一個Survivor區佔整個年輕代的1/10
在New Generation中,有一個叫Eden的空間,主要是用來存放新生的對象,還有兩個SurvivorSpaces(from,to), 它們用來存放每次垃圾回收後存活下來的對象。在Old Generation中,
主要存放應用程序中生命週期長的內存對象,還有個Permanent Generation,主要用來放JVM本身的反射對象,好比類對象和方法對象等。
從上面的圖能夠看出來JVM的新生代設置過小,能夠看出應用的新生代區徹底佔滿了,沒法再往新生代區增長新的對象此時的這些對象都處於活躍狀態,因此不會被GC處理,可是tomcat應用還在繼續產生新的對象,
這樣就會致使OOM的發生,這就是致使tomcat假死的緣由.
如下是網上資料說的tomcat假的狀況:
一、應用自己程序的問題,形成死鎖。
二、load 過高,已經超出服務的極限
三、jvm GC 時間過長,致使應用暫停
由於出錯項目裏面沒有打出GC的處理狀況,因此不肯定此緣由是否也是我項目tomcat假死的緣由之一。
四、大量tcp 鏈接 CLOSE_WAIT
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
TIME_WAIT 48
CLOSE_WAIT 2228
ESTABLISHED 86
============================================nginx
1) 調高web服務器的最大鏈接線程數,即打開tomcat的server.xml文件,設置 Connector的如下參數:minProcessors="70" maxProcessors="2000" acceptCount="2000"
2) 修改運行web服務器的機器的操做系統網絡配置,即在註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters中增長如下兩個DWORD參數:
tob_id_3433
MaxUserPort=fffe TcpTimedWaitDelay=1e 最後,從新啓動計算機。