昨天服務器的CPU忽然100%,此服務已經運行幾年了,都平安無事。既然問題出現固然要找出這個遺留多年的小几率問題。出現cpu 100% 通常就是哪裏出現了沒法跳出的死循環。
安全
服務器使用的window server 直接右鍵建立轉儲文件便可。這個直接點點的方式是使用window server最方便的地方(^_^)。服務器
將文件複製本地,直接拖拽到windbg中。(windbg直接在window 應用商店下載便可)多線程
在0:000 輸入框中輸入!runaway 敲回車,獲取線程佔用cpu時間spa
從cpu的線程佔用時間來看,線程64,89,96,95,90佔用的時間最長,能夠初步判定問題就出如今這幾個線程中。輸入:~64s進入該線程(64表明的是線程id)線程
進入該線程後就能夠加載線程的棧信息了,在命令框中輸入!CLRStack 。若是出現圖示信息說明須要單獨加載SOS.dll 文件。使用everything軟件全局搜索該dll文件位置,而後在輸入框中:.load 全路徑\SOS.dll。加載完成再次輸入:!CLRStack 就能夠正常顯示棧信息了。3d
從棧信息來看,線程一直停留在:System.Collections.Generic.Dictionary`2[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].FindEntry(System.__Canon)這個方法裏面,調用這個方法的類是:Aop.Api.Parser.AopJsonParser,這個類是支付寶官方sdk中的。先看FindEntry這個方法幹了啥。直接官網查看源碼:code
private int FindEntry(TKey key) { if( key == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); } if (buckets != null) { int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next) { if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i; } } return -1; }
從源碼來就是判斷key是否存在,看來就是死循環for裏面了。再來看這個dictionary是用來幹啥了的,反編譯Aop dll文件server
private static Dictionary<string, AopAttribute> GetAopAttributes(Type type) { Dictionary<string, AopAttribute> dictionary = null; if (!AopJsonParser<T>.attrs.TryGetValue(type.FullName, out dictionary) || (dictionary == null)) { dictionary = new Dictionary<string, AopAttribute>();
private static readonly Dictionary<string, Dictionary<string, AopAttribute>> attrs;
從源碼能夠看見定義了一個靜態Dictionary attrs變量,既然是靜態變量,會出現多線程競爭問題。官網也明確說明Dictionary 是非線程安全的,若是多線程讀寫須要本身去寫程序保證線程安全或者使用ConcurrentDictionary。blog
問題的根本緣由是多線程多寫非線程安全Dictionary,致使在FindEntry方法死循環。進程