性能測試中你是否遇見過頻繁fgc的問題呢?

前言:今天分享一個頻繁fgc的問題,現象是接口響應時間太長了,達到了好幾秒,遠遠高於預期的1秒。

xshell連接到應用服務器,服務器負載高,且cpu使用率也偏高。

在這裏插入圖片描述

使用jstat看了下gc的情況,fgc很頻繁,老年代滿了(下圖的O列)

在這裏插入圖片描述

打開JvisualVM,雙擊對應的應用進程,然後進入Monitor,可以看到堆內存GC頻繁。然後進入 Visual GC查看,發現堆內存Full GC非常頻繁.

在這裏插入圖片描述

根據下面gc日誌,Full GC Old區回收的內存很少。

在這裏插入圖片描述

現在情況就非常明顯了,就是內存中有大量GC不掉的對象。

下面我們看看heap裏面到底是些什麼東西。切換到monitor,點擊heap dump生成dump文件,然後把dump文件拷貝到本機進行分析。

在這裏插入圖片描述

分析dump文件,點擊class,根據size排序,找到比較大且比較熟悉的實例,下面對紅框中兩個class進行分析。

在這裏插入圖片描述

sacToNoticeConfirmRequest這個class是業務接口,但是現在應用程序並沒有請求sacToNoticeConfirm這個接口,爲啥這個class會有這麼多對象呢?

先分析其他對象,ConstraintViolationimpl這個class是dubboRPC請求對參數進行驗證時需要的類,看看這個類的實例都是些什麼內容:

在這裏插入圖片描述

看了多個實例後發現ConstraintViolationimpl這個class的實例對象都是因爲RPC參數校驗不通過生成的對象。我們在sacToNoticeConfirmRequest這個類裏面使用了javax.validation這個包的註解,如果sacToNoticeConfirmRequest請求參數在RPC調用時參數校驗不通過就會產生大量的ConstraintViolationimpl實例。我們繼續分析sacToNoticeConfirmRequest這個class的實例:

在這裏插入圖片描述

終於知道這些GC不掉的內存是哪裏來的了😭。猜想應該是壓測的時候沒有填寫參數的原因(其實是可以不填參數就請求接口),而我們的接口用了Dubbo的參數校驗這個特性,那麼在Dubbo框架層就會校驗這些參數,校驗不通過就會產生大量的ConstraintViolationimpl實例(這些實例又引用了很多HashMap$Node實例)。填寫參數壓測,就沒出現頻繁fgc的情況。

現在找到問題了,就是因爲使用Dubbo參數驗證這個特性,在大多數請求參數校驗不通過的情況下就會出現大量對象GC不掉,導致頻繁FULL GC,最終導致響應時間很慢的情況。

至於爲什麼在這種情況下大量的對象GC不掉,猜測這個可能是Dubbo參數校驗的bug,所以建議不要使用Dubbo的參數校驗,可以使用Dubbo的本地存根(stub)這個特性來替代Dubbo的參數校驗。

Dubbo的本地存根:在遠程調用服務提供者的實現之前,提供方有時候想在客戶端也執行部分邏輯,如果需要做一些參數驗證、判斷等,滿足要求再調用服務提供者的遠程服務,則我們可以通過編寫一個本地存根來實現這種功能。

dubbo本地存根的原理,大家可以網上查詢瞭解。

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