閱讀722 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Java性能調優隨記

事情發生在16年了。當時係統beta版本進行上線前的性能壓測時,發現進程的內存占用率會持續升高,與之而來的時,性能的接口性能的持續下降。最奇怪的是,停止壓測後CPU和內存開銷並沒有恢複過來。記得之前發過博文,不知道怎麼回事找不到了。 應其他同事的要求,回憶一下當時的定位過程,再做一個簡單分享。
在講述整個過程前,請大家自備兩個梯子:
1)Java虛擬機的垃圾回收機製
2)jmeter的基本使用知識

首先下載jmeter工具,感覺是一個輕量級的性能檢測工具,很好用。有基於windows 的GUI版本,也有在linux上運行的命令行版本。
下載地址:https://jmeter.apache.org/download_jmeter.cgi

怪象簡述
剛開始壓測時,一切指標都是正常的,也達到了預期水平。
image

8小時後,性能直線下降。
image

停止壓測後,CPU和內存,仍然居高不下。 不要小看 2.4%的內存占用,這是200多G內存的物理服務器。(後麵部署方式已切換為虛擬機的部署方式)
image

定位解決
1) 首先命令行,看下java堆的情況:jmap -heap 24442
看見年老區的內存被占滿了。
image

2) 繼續執行命令:jsat –gcutil 24442 1000 5,
看見年老區 使用率 100%,同時執行了12304次 FullGC
image

3)查看進程24442的線程信息: ps –mp 24442 –o THREAD,tid,time
發現很多線程運行了好幾個小時
image

這個時候,明顯懷疑有內存泄露了。
再執行了兩個命令(時間太久,沒有找到截圖了):
jstat -printcompilation -h3 24442
jmap -histo 24442

進一步發現,內存中某個類的實例數量和String類的實例數量,異常的多。
最後在排查代碼,找到了具體的類 及其使用邏輯。發現是由於隊列的使用不當,造成了內存泄露

總結
本項目中,因為涉及頻繁的小IO,所以開發同事期望通過生產者-隊列-消費者的模型,用批量IO解決頻繁小IO帶來的資源開銷。但在使用此模型時,忘計考慮了極限狀態下,生產者的輸入能力遠大於消費者的消費能力時,會造成隊列中的數據積壓,進而造成內存泄露。但又因為常規情況下不會出現 此場景,所以很難發現這個bug。
這個案例告訴我們:
1) 對於一些關鍵接口,開發同事可以在完成開發後,自己用Jmeter壓測一下,避免問題遺留到上線前期,造成巨大風險。
2) Java仍然會有內存泄露的情況,使用類似數組、隊列、棧等數據結構時,需要格外小心。

最後更新:2017-11-09 18:03:35

  上一篇:go  阿裏雲播放器單擊切換播放/暫停
  下一篇:go  如何用Go實現單鏈表