標籤:style blog code c http width
最近項目突然收到了一個緊急的問題報告 - 使用者在進行某些關鍵操作的時候整個軟體突然就crash掉了。幸好產品繼承了自動抓取dump的功能。。。
收到dump之後,通過windbg開啟,查看相應的callstack。迅速定位到出錯的線程。在調用MapViewOfFile()的時候沒有map成功導致crash。 接下來,我們需要知道為什麼為何map失敗?通過GetLastErrorCode(),我們得知進程OutOfMemory導致map失敗。MapViewOfFile()需要映射512KB(4Mb)連續記憶體,而通過!address -summary命令,我們發現系統裡最大的連續記憶體塊為1.5Mb。Crash因此發生了。 至此,我們可以肯定,該crash是由於memory leak造成MapViewOfFile()失敗。那麼,是什麼leak了?重新看上面的輸出資訊,我們可以看到,MEM_MAPPED使用的記憶體高達934Mb,MEM_PRIVATE使用的記憶體為805Mb。據此,我們懷疑,產品裡所使用的memory mapped file可能會存在大量的leak。 運行命令!address -f:MEM_MAPPED,會得到類似的輸出: 第三列,也就是RgnSize,為當前MMF的映射視窗大小。查看了該輸出,我們發現,視窗大小為512kB的檔案在500個左右,而大小為256KB的檔案多大1800個。據此,我們可以鎖定這些256KB的MMF檔案。 下面的問題,就是,誰映射了這麼多的MMF檔案?在此不同的項目可以有不同的方法。如果你很熟悉項目資料的話,你可以先通過dd查看一下該256KB的內容都是啥?有沒有你所熟悉的內容?如果是字串的話那就更容易定位問題的所在地方了。 這裡,我們假設我們不熟悉檔案的內容,從而無法猜測這些資料是誰產生的。我們只能採取其他辦法。 上面輸出的第一列,也就是BaseAddr,為該map view的地址。如果我們要使用該map view的話,我們必須要有對應的指標指向該view。因此,我們隨機選了一個256KB的view的baseaddress,然後通過下面的命令尋找誰還指向了這塊記憶體。 從該輸出我們能夠看到,大概有10多個地方儲存有該指標地址。接下來,我們就需要查看誰,也就是那些類的instance還儲存這些地址。此處沒有其他好的辦法,只能在這些地址四處通過dds命令來查看是否有對象的symbol。 在查看了多個地址之後,我們發現,有一個vftable為CMMFSerializer的對象。這給了我們極大的肯定:該類名裡有MMF!十有八九是該類的leak! 接下來,我們的目標為誰建立了CMMFSerializer對象而沒有銷毀?首先,我們通過x命令來獲得CMMFSerializer的虛表地址,這樣我們就能夠在heap裡搜尋這些對象都存在哪裡。 然後,運行!heap命令,就能夠查看到擁有這些對象的地址了。從我們的輸出能夠看到,大量的CommunicatorObj對象存在heap裡。這就說明了CommunicatorObj這些對象沒有被刪除掉。從而,我們可以做這樣的結論:CommunicatorObj的泄漏導致了如此之多的MMF的泄漏,以及很高的MMF_PRIVATE使用率如此之高。剩下來的工作就很簡單了,看看代碼,分析分析哪些地方使用了CommunicatorObj。