捉蟲記(二)GC導致的hang


本來第二篇早就該寫的,都准備寫一個while(true)導致高CPU的問題,但是那個dump不知道跑到哪里了,害得我這段時間一直在找,這次的現象是,哥正准備出去泡個茶,突然業務經理嗷嗷直叫(終於找到一個Bug,能不興奮么),一邊喊着處理不行了,一邊喊着趕緊重新啟動,哥沒有吱聲,默默打開了性能計數器,看到處理程序的緩存的確是慢慢在增長(這表示有積壓了),而處理速度降到非常低了,幾秒鍾處理一個(平時情況下哥的程序每秒鍾處理1000個Socket業務包是 沒有問題的),說到這里建議大家在自己的程序中也加入一些自定義性能計數器,這東西太好用,當你把系統的一些值(比如說CPU的UserTime,還有Disk的DQL等)和你自己程序的一些值(比如說程序的緩存數,處理的並行進程數等)以文件方式記錄下來的時候,做個Excel圖表,這樣你就能對整個系統的性能有了一個很直觀的認識。

1. 首先對程序有個大概的了解。

內存1.2G,程序已經運行好幾天了。

無標題

 

2. 既然是程序處理速度變慢,那么應該看看CPU,線程池的消耗情況等

看看線程如何:

第一個命令!runaway(如果你是Web程序,那么這個命令對你講非常有用,但是這個From程序中,很多模塊用了那種該死的While(true),基本上這個命令我們得不到什么東西)

無標題1

讓我們看看線程池是什么樣子吧(!tp這個命令可以看到線程池的情況):

無標題2

Wowowo…,看這個cpu很高啊。

3. 我們看看我們的處理線程在看什么吧(!thread能看到所有的線程)。

無標題3

。。。。。。

無標題4

哦,注意那個32號, GC Disabled,到這里我們大概猜測到是GC將處理線程阻塞掉了。

4.為了進一步驗證我們的猜測,我們看看線程們到底在干什么吧(~*e !clrstack這個命令將打印所有線程棧,別着急,慢慢看,這個可以copy一下,貼到一個比較容易查看的編輯器里慢慢看)。為了看起來方便,我把打印結果copy出來用notepad++來看看,下面這個截圖,我做過處理,不然太多了,看不過來的。

無標題5

 

5.我的處理函數是SchemeEntity.ProcessIMSIContext,並且開了十個線程處理(都是從線程池申請,最多同時允許十個線程處理,如果超過是個線程都沒有處理完畢,那么分發線程將等待,新來的請求將在緩存隊列中,就是上面所說的那個正在慢慢增長的的緩存),從這個可以看出,十個處理線程都在處理,那么我們看看他們都在干嘛,隨機挑選兩個37,38號看看吧。

clip_image016

clip_image018

6.哦,他們都在Monitor.Enter了,我們重點看一下32,切換過去吧(切換線程 ~線程號s)

clip_image020

7.哦哦~,看到了么,38,37都在等待,但是32在GC,我們看看32的堆棧吧,果然在GC。(kv顯示非托管棧)

clip_image022

看看托管的吧(!clrstack),哦哦~List.set_Capacity(32) ,這幾乎可以肯定是List在做copy的動作,List將申請自己現在長度的兩倍的內存,應該是這個長度非常非常長,現有托管堆內存不大夠,所以引起長時間的GC了。

clip_image024

8.我們直接去看看這行代碼到底在干嘛吧,看到藍色噴霧了吧,那個地方可以對應到代碼的行數(我的截屏上只有D:\I 這個樣子),屏幕太小了,我沒法直接貼出來。但是上面顯示是第64行。

clip_image026

我們可以看到iMsiDayCountDic是一個ConcurrentDictionary,但是用.Keys.Contains這個用法,額,我們可以想到, Key這個應該是一個ICollection,然后再上面調用Contains應該是新生成一個List<ulong>,然后把Dict里面的key全部copy到這個List<ulong>中,當Dict里面的數據非常多的時候,在生成List<ulong>的過程中,List<ulong>需要每次不斷申請自身長度的2倍,於是慢慢就GC了,整個處理被阻塞了,其實Dict有另外的方法了

clip_image028

我們修改成這個樣子,就OK了。

------------------------------------------------------------------------------------------------------------------------------------------

總結:

1. 對程序大概了解,知己知彼。

2. 看看是不是GC阻塞了線程(!thread這個命令看看GC有沒有Disenable)。

3. 切換到被阻塞的線程看看棧,看看問題的代碼。

4. 我們也能看的出,如果系統中有互斥資源(這些資源每次只能被一個線程所訪問),那么通過提高線程數量來提高處理速度是有限的,特別是lock里的代碼消耗了非常長的時間的時候。

 

(用live wirter寫的,好像排版還是有些問題,我會努力改正,另外很多圖片為了公司的保密,我不得不做了一些處理,如果不方便看的地方,請直接問我,我會詳細給大家說清楚) 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM