記一次記憶體持續增長問題排查
記一次記憶體持續增長問題排查
作者:張鑫
發生背景:
測試同學執行 AElf 單節點過程中,發現節點突然不再出塊,經檢視日誌發現 Worker (交易執行程序) 全部掉線,無法繼續執行交易,從而導致節點掛掉。
初步定位問題:
出現這個問題很奇怪,因為節點和所有 Worker 在同一臺伺服器上,網路通訊應該不會有問題,再者發現,主節點、所有 Woker 和 Lighthouse 幾乎在同一時間全部掉線。然後繼續排查,通過 zabbix 監控找到了問題,伺服器在一個時間點記憶體幾乎被耗盡,通過觀察時間,發現與節點出現異常時間吻合。
復現問題:
我們重點對記憶體使用進行測試。測試發現,隨著節點長時間執行,程序佔用伺服器記憶體在不斷增加,尤其在持續發了大量交易後,記憶體使用增長明顯,並且停止發交易後,記憶體也並不會下降。
下面是具體的復現步驟:
首先介紹服務環境,我們使用 Ubuntu 16.04.5 LTS , dotnet core 版本為 2.1.402
節點剛開始執行的情況:記憶體使用大約為 90M
然後對節點持續發大量交易,我們可以通過監控看到節點交易池堆積大量交易並不斷在執行
持續一段時間後,記憶體佔用已經達到 1G
此時停止發交易,等待交易執行,我們通過監控看到交易池中的交易已都被執行。
等待一段時間後,此時我們繼續觀察記憶體佔用,發現記憶體使用還是不會減少,一直保持 1G 的水平
分析問題:
接下來我們使用 lldb 分析我們的節點。
首先在伺服器上安裝 lldb
sudo apt-get install lldb
找到本機 ibsosplugin.so 位置
find /usr -name libsosplugin.so
啟動 lldb 並附加到程序
sudo lldb –p 13067
載入 libsosplugin.so
plugin load /usr/share/dotnet/shared/Microsoft.NETCore.App/2.1.4/libsosplugin.so
setclrpath /usr/share/dotnet/shared/Microsoft.NETCore.App/2.1.4/
首先分析下物件
dumpheap -stat
我們可以看到有大量的以下物件
AElf.Kernel.TransactionHolder
System.String
AElf.Common.Address
System.Collections.Concurrent.ConcurrentDictionary`2+Node[[AElf.Common.Hash,AElf.Common],[AElf.Kernel.TransactionHolder,AElf.Kernel.TxHub]]
AElf.Kernel.Transaction
AElf.Common.Hash
Google.Protobuf.ByteString
System.Byte[]
我們再看下大於 1024 位元組物件
可以看有 4 個物件同類型的物件比較大
System.Collections.Concurrent.ConcurrentDictionary`2+Node[[AElf.Common.Hash, AElf.Common],[AElf.Kernel.TransactionHolder, AElf.Kernel.TxHub]][]
再進一步檢視 MethodTable對應的物件
可以看到有 8 個物件,其中 4 個較大,挑選其中一個檢視物件資訊,發現裡面儲存了 573437 個值。
基於以上分析結果,排查對應原始碼,定位到類: AElf.Kernel.TxHub 。該類主要作用是儲存交易池交易資料。該類包含 8 個 ConcurrentDictionary<Hash, TransactionHolder> 用於儲存交易資料,而 TransactionHolder 類中有儲存了 Hash 、 Transaction 等型別,與上面記憶體分析的結果吻合。再繼續看內部邏輯,發現所有交易進入交易池後一直儲存在 TxHub 中,不再進行釋放。到此為止基本上定位問題所在。
待問題修復後重覆上面步驟進行驗證,效果比較明顯,待交易池交易執行完畢後,記憶體佔用有明顯下降,最終記憶體佔用如下
繼續看下記憶體中物件的情況,可以看到物件總數也有了明顯的下降。
但是仍存在記憶體少量增長的現象,並且有大物件駐留記憶體的情況,此問題會進一步跟進分析。