目前性能測試組正在對獨立秒殺進行性能壓測,性能抖動特別厲害。
由於獨立秒殺的接口大多數是經過volicity渲染過的頁面和數據的整合,所以在壓測的時候有很多volicity的錯誤。初步判定,感覺是volicity的性能問題才導致的。但是通過排查volicity發現,此版本沒有網傳的性能問題,而且代碼層面上也沒見到有過多的性能問題點。
之后通過查看jvm的堆內內存才發現,老年代的內存無法釋放,總是會經過很長一段時間,大概三十四分鍾后才會釋放。感覺很奇怪:
從上圖可以看到,堆內內存漲上去后,基本上就下不來了, 這些沒釋放的內存,基本上都在老年代。初步判定為jvm堆內內存要么有大對象,要么什么東西一直持有,並未釋放。
之后從服務器上dump數據下來,然后通過mat加載后,得到的分析如下:
可以看到,系統中,有一個ConcurrentHashMap的容器里面,貌似對每個http請求,都做了一次緩存。考慮到目前做的是壓測,那么也就是說瞬間涌入千萬級別的請求也不為過,ConcurrentHashMap的體積在很短的時間就會暴漲,勢必會帶來頻繁的gc問題。如果只是保存http請求狀態,為什么http請求完畢,不會釋放呢?
帶着疑問去應用里面進行排查,發現應用里面根本沒有直接使用ConcurrentHashmap對象。那么也就是說ConcurrentHashmap對象也許是存在什么jar包里面了。經過排查jar包,也沒發現什么地方使用concurrenthashmap,頓時陷入了死局。
后來,壓測組發來一篇文章:壓力測試中JVM內存暴漲原因分析實戰, 看完文章,和我的遭遇非常一致,聯想到目前壓測直接使用ip+端口壓測,直接打到tomcat上進行壓測,而且接口返回數據都是經過velocity渲染的模板和數據組合,是有前端頁面的。所以說,按照文章中的說法,應該是tomcat對每一個進來的請求都會將狀態會話保持放到ConcurrentHashmap中導致的,而且這個狀態會話保持默認30分鍾后過期,這也是為啥GC一直下不來的原因了。
為了印證此說法,按照此文的建議, Memory Fully utilized by Java ConcurrentHashMap (under Tomcat),在web.xml中設置session過期時間為1分鍾:
<session-config>
<session-timeout>1</session-timeout>
</session-config>
之后修改代碼,上預發,然后讓壓力機單壓預發這台機器,可以看到堆內存回收如下:
可以看到當堆內存打到極高點后,jvm很快進行了一次回收,而且此次回收比較徹底。
驗證完畢,看來是這個原因。希望對你有幫助。