Java服務記憶體溢位問題解決和總結
最近,公司測試環境服務發現一個問題:一個介面服務,合作方再調介面時,經常會出現連線超時異常(connection reset by peer),緊接著看到服務記憶體100%,加記憶體也沒用,不管加多少還是會緩慢升至100%。如下圖:
通過各位大神的指點迷津,大概定位到以下問題:
- 程式出現記憶體洩漏,但洩漏不是太嚴重。
- 物件進入老年太,肯定有大量程式碼使用記憶體超過1M
對於1和2這兩個問題,都表面程式碼肯定是有問題的。然後各位大佬開始出謀劃策:
- 再加1個g觀察是否繼續到100%
- 用-Xms1024m -Xmx1024m限制jvm記憶體,根據使用情況 限定記憶體 超過後自行垃圾回收
- new超過1M的,將物件進入 物件池。物件池會反覆減少gc
- 通過jstack定位哪些記憶體沒有釋放
對於2這種直接限制jvm記憶體的做法,能快速解決目前的問題,但是如果要求併發,增加處理速度,就必須改程式碼了。剛好這又是個介面程式,高併發實時性要求很嚴格。所以治標先治本,先用jstatck定位問題。
由於博主是小白,先學習一波jstatck命令:
jstatck簡介:
jstack用於生成java虛擬機器當前時刻的執行緒快照。執行緒快照是當前java虛擬機器內每一條執行緒正在執行的方法堆疊的集合,生成執行緒快照的主要目的是定位執行緒出現長時間停頓的原因,如執行緒間死鎖、死迴圈、請求外部資源導致的長時間等待等。 執行緒出現停頓的時候通過jstack來檢視各個執行緒的呼叫堆疊,就可以知道沒有響應的執行緒到底在後臺做什麼事情,或者等待什麼資源。 如果java程式崩潰生成core檔案,jstack工具可以用來獲得core檔案的java stack和native stack的資訊,從而可以輕鬆地知道java程式是如何崩潰和在程式何處發生問題。另外,jstack工具還可以附屬到正在執行的java程式中,看到當時執行的java程式的java stack和native stack的資訊, 如果現在執行的java程式呈現hung的狀態,jstack是非常有用的。
先來補習一波linux命令:
- top:在linux環境下,可以通過
top
命令檢視各個程序的cpu使用情況,預設按cpu使用率排序; - top -Hp pid:通過
top -Hp 23344
可以檢視該程序下各個執行緒的cpu使用情況,預設按cpu使用率排序; - jps -l:檢視當前使用者下的所有java程序 ,在root許可權下及檢視所以java程式程序。
上面兩個命令可以看出pid為25077的執行緒佔了較多的cpu資源,利用jstack命令可以繼續檢視該執行緒當前的堆疊狀態。
執行緒對應的pid轉成十六進位制去dump檔案查詢,對應就是出問題的地方。
jstack命令
通過top命令定位到cpu佔用率較高的執行緒之後,繼續使用jstack pid
- jstack pid :檢視dump檔案,直接顯示
- jstack -l pid > pid.stack:形成檔案,從伺服器下載下來後放到 IBM Thread and Monitor Dump Analyzer for Java分析工具中分析。
分析結果如下:
分析報告出來了,就需要知道每項指標的含義:
1.dump 檔案裡,值得關注的執行緒狀態有:
死鎖, Deadlock(重點關注)
執行中,Runnable
等待資源, Waiting on condition(重點關注)
等待獲取監視器, Waiting on monitor entry(重點關注)
暫停,Suspended
物件等待中,Object.wait() 或 TIMED_WAITING
阻塞, Blocked(重點關注)
停止,Parked
2.Dump檔案中的執行緒狀態含義及注意事項
Deadlock:死鎖執行緒,一般指多個執行緒呼叫間,進入相互資源佔用,導致一直等待無法釋放的情況。
Runnable:一般指該執行緒正在執行狀態中,該執行緒佔用了資源,正在處理某個請求,有可能正在傳遞SQL到資料庫執行,有可能在對某個檔案操作,有可能進行資料型別等轉換。
Waiting on condition:該狀態出現線上程等待某個條件的發生。具體是什麼原因,可以結合 stacktrace來分析。最常見的情況是執行緒在等待網路的讀寫,比如當網路資料沒有準備好讀時,執行緒處於這種等待狀態,而一旦有資料準備好讀之後,執行緒會重新啟用,讀取並處理資料。在 Java引入 NewIO之前,對於每個網路連線,都有一個對應的執行緒來處理網路的讀寫操作,即使沒有可讀寫的資料,執行緒仍然阻塞在讀寫操作上,這樣有可能造成資源浪費,而且給作業系統的執行緒排程也帶來壓力。在 NewIO裡採用了新的機制,編寫的伺服器程式的效能和可擴充套件性都得到提高。
如果發現有大量的執行緒都在處在 Wait on condition,從執行緒 stack看, 正等待網路讀寫,這可能是一個網路瓶頸的徵兆。因為網路阻塞導致執行緒無法執行。一種情況是網路非常忙,幾 乎消耗了所有的頻寬,仍然有大量資料等待網路讀 寫;另一種情況也可能是網路空閒,但由於路由等問題,導致包無法正常的到達。所以要結合系統的一些效能觀察工具來綜合分析,比如 netstat統計單位時間的傳送包的數目,如果很明顯超過了所在網路頻寬的限制 ; 觀察 cpu的利用率,如果系統態的 CPU時間,相對於使用者態的 CPU時間比例較高;如果程式執行在 Solaris 10平臺上,可以用 dtrace工具看系統呼叫的情況,如果觀察到 read/write的系統呼叫的次數或者執行時間遙遙領先;這些都指向由於網路頻寬所限導致的網路瓶頸。另外一種出現 Wait on condition的常見情況是該執行緒在 sleep,等待 sleep的時間到了時候,將被喚醒。
locked:執行緒阻塞,是指當前執行緒執行過程中,所需要的資源長時間等待卻一直未能獲取到,被容器的執行緒管理器標識為阻塞狀態,可以理解為等待資源超時的執行緒。
Waiting for monitor entry 和 in Object.wait():Monitor是 Java中用以實現執行緒之間的互斥與協作的主要手段,它可以看成是物件或者 Class的鎖。每一個物件都有,也僅有一個 monitor。
定位到是Waiting on condition問題,可能是介面服務回撥超時等待404,把所有客戶方的回撥地址確認一波,看看情況~
未完待續,下回繼續更!