程序一天部署好幾次,沒辦法,bug太多了,催的又急,每次修改一下就趕緊部署了,某天突然有個經理電話里喊道,你這程序內存怎么越用越大,幸好哥早有防備,呃,經理,先別着急,請雙擊一下桌面的“Server.Bat”,然后重新啟動一下程序(重新啟動一下內存就小了哈)。
1。首先對程序的運行有個大概的了解。(比如說 程序運行了多長時間,內存多大)
登到服務器上一看,我的天, dump文件1.8G---程序大概是2個小時之前部署
打開windbg,打開dump,呃,程序運行了2個半小時。
設置上符號地址,然后加載sos
2。查看GC堆上的內存占用是多大。(一般來講,內存泄露都是GC堆上的問題,也就是說new了一些對象在某些情況下沒有釋放,!EEHeap -GC 正是查看GC堆得命令,我們看到
最后一行GC Heap Size是1.2G,1.8G的內存,GC堆1.2G,肯定有問題了,當然,Loader堆也不大可能有600M,以后我會對Loader堆繼續分析。。。)
第一個命令 !EEHeap -GC
3。確定是GC堆的問題,就看看GC堆上存活的對象是那些。(!dumpheap -stat 就是GC堆上的統計,我們看到統計結果,第一行SingnallingContex, 00000644803111a0 是該對象的MethondTable的地址,而186402是該對象在內存中的個數,35789184是大小,這是純凈的大小,該對象里面所包含的對象的大小不算在內,看到Context相關聯的ProcessDetail竟然有3768463個,所占字節為211033928,這足夠驚人了。。。)
呃,看到這里都能猜想到,Loader堆里肯定有很多垃圾,先不管這么多了[我會在后續中介紹Loader堆里面的事情],先看看GC堆里有什么,第二個命令!DumpHeap -stat。
看到一個很熟悉的名字 “SingallingContext”,這是一個Context,這個Context是接收到數據之后包裝一下經過業務的pipfilter。1W多個,而Context上多呆的ProcessDetail竟然到了百萬,猜測是某個處理Module緩存住了,沒有釋放。
4。確定了是哪種類型對象的問題,那就看看該對象。( !Dumpheap -mt 00000644803111a0 你也可以用 !Dumpheap -type SingnallingContex 這個命令)
於是 第三個命令 !Dumpheap -mt 00000644803111a0
5。找到其中一個對象,看看是因為誰的引用而沒有釋放(!gcroot +對象地址 這個命令就可以得到這個對象的“根”,這個過程會非常長,具體講該命令將會遍歷所有的根,我們看到Scan Thread 67 實際上就是在掃描67號線程的線程Stack是不是引用了該對象。)。
額,這么多,隨便找一個,就找最后一個吧,第四個命令 !gcroot 00000001677ceda8,這個時候該去廁所抽支煙....
6。我們找到了“罪魁禍首”的對象,一般我們看看代碼就OK了。(這里閑的無聊的話我們可以看看這個對象到底占了多大的內存,!objsize +對象地址,我們就看到了,這個命令時間非常長,長到你可以蹲着抽煙)
額,看到根了,SingallingContextTracer,這個東西是個跟蹤器,不會是跟蹤器沒有釋放吧??先不管,看看多大,第五個命令 !objsize 00000000c1707658
打開代碼,找到SingallingContextTracer,呃,這個可憐的家伙把每個Context放到自己的Queue中,然后開啟另外的線程來處理,可惜處理線程被誰注釋掉了(跟蹤功能因為沒啥用處,被去掉了,但是去掉了應該不讓入隊啊),好吧,趕緊修改代碼,重新部署......
BTW:貼圖排版好累啊,怎樣容易排版一些啊?
-----------------------------------------------------------------------------------------------------
總結:
1.對程序大概了解,知己知彼。
2.確定什么地方引起的泄露,是GC堆還是什么Loader堆等。
3.確定是哪種類型引起的泄露,其實可以dump兩次,然后通過比較兩次對象的不同,更加明顯看出是那個類型的對象出了問題。
4.找到該對象。
5.找到該對象的root。
root都找到了,剩下的問題就都好解決了。。。。