記一次 .NET 某打印服務 非托管內存泄漏

一:背景

1. 講故事

前段時間有位朋友在微信上找到我,說他的程序出現了內存泄漏,能不能幫他看一下,這個問題還是比較經典的,加上好久沒上非托管方面的東西了,這篇就和大家分享一下,話不多說,上 WinDbg 說話。

二:WinDbg 分析

1. 到底是哪里的泄漏

好的開始就是成功的一半,否則就南轅北轍了,對吧,還是用經典的 !address -summary 看一下內存排布情況。

0:000>?!address?-summary---?Usage?Summary?----------------?RgnCount?-----------?Total?Size?--------?%ofBusy?%ofTotal
Heap???????????????????????????????????1935??????????553b3000?(???1.332?GB)??70.57%???66.59%
Image??????????????????????????????????1022???????????c306000?(?195.023?MB)??10.09%????9.52%
<unknown>??????????????????????????????1202???????????c09d000?(?192.613?MB)???9.97%????9.41%
Stack???????????????????????????????????541???????????b280000?(?178.500?MB)???9.24%????8.72%
Free???????????????????????????????????1158???????????73ab000?(?115.668?MB)????????????5.65%
TEB?????????????????????????????????????180????????????20f000?(???2.059?MB)???0.11%????0.10%
Other?????????????????????????????????????8?????????????5d000?(?372.000?kB)???0.02%????0.02%
PEB???????????????????????????????????????1??????????????3000?(??12.000?kB)???0.00%????0.00%---?State?Summary?----------------?RgnCount?-----------?Total?Size?--------?%ofBusy?%ofTotal
MEM_COMMIT?????????????????????????????3077??????????643c6000?(???1.566?GB)??83.00%???78.31%
MEM_RESERVE????????????????????????????1812??????????1487f000?(?328.496?MB)??17.00%???16.04%
MEM_FREE???????????????????????????????1158???????????73ab000?(?115.668?MB)????????????5.65%

從卦中可以看出,當前 MEM_COMMIT = 1.56 G, 并且 Heap= 1.3 G,既然超出了朋友的預期,很明顯這是一個非托管內存泄漏,既然 NTHeap 出現了泄漏,那就挖一下看看,使用 !heap -s 觀察一下各個heap句柄。

0:000>?!heap?-s************************************************************************************************************************NT?HEAP?STATS?BELOW
************************************************************************************************************************
LFH?Key???????????????????:?0xbb72f2a3
Termination?on?corruption?:?DISABLEDHeap?????Flags???Reserv??Commit??Virt???Free??List???UCR??Virt??Lock??Fast?(k)?????(k)????(k)?????(k)?length??????blocks?cont.?heap?
-----------------------------------------------------------------------------
00770000?00000002???16576???9716??16364?????33???195?????5????0??????0???LFH
006f0000?00001002????1292????148???1080?????11?????4?????2????0??????0???LFH
00a80000?00001002????3336???1972???3124?????88????25?????3????0??????0???LFH
02460000?00001002??????60??????4?????60??????0?????1?????1????0??????0??????
023b0000?00041002??????60??????4?????60??????2?????1?????1????0??????0??????
02450000?00001002?????272?????24?????60??????1?????3?????1????0??????0???LFH
04a40000?00041002????1292?????80???1080??????8?????4?????2????0??????0???LFH
06e90000?00001002???64180??56660??63968???1434???473?????9??624??????7???LFH
09dc0000?00001002??????60?????12?????60??????3?????2?????1????0??????0??????
0a500000?00001002????7428???3772???7216?????43????35?????4????0??????0???LFH
-----------------------------------------------------------------------------

從卦中的 Commit 列來看,內存占用都不大,最大的也不過 56M ,如果經驗豐富的話,你會發現 Virt blocks 高達 624 個,明白 ntheap 的朋友應該知道,凡是大于 512kheapentry 都會單獨安排到 VirtualAllocdBlocks 數組中,可以用 dt ntdll!_HEAP 06e90000 給show出來。

0:000>?dt?ntdll!_HEAP?06e90000...+0x05c?VirtualMemoryThreshold?:?0xfe00+0x09c?VirtualAllocdBlocks?:?_LIST_ENTRY?[?0x6ea4000?-?0x7c0d0000?]...

為了更好的輸出 VirtualAllocdBlocks 數組,我們用 windbg 自帶的 heap 分析命令。

0:000>?!heap?06e90000?-m
Index???Address??Name??????Debugging?options?enabled8:???06e90000?Segment?at?06e90000?to?06e9f000?(0000f000?bytes?committed)Segment?at?078f0000?to?079ef000?(000ff000?bytes?committed)Segment?at?08870000?to?08a6f000?(001ff000?bytes?committed)Segment?at?0ec60000?to?0f05f000?(003f9000?bytes?committed)Segment?at?18660000?to?18e5f000?(007fa000?bytes?committed)Segment?at?26b20000?to?27aef000?(00fc0000?bytes?committed)Segment?at?45320000?to?462ef000?(00fcf000?bytes?committed)Segment?at?65bf0000?to?66bbf000?(008bf000?bytes?committed)Flags:????????????????00001002ForceFlags:???????????00000000Granularity:??????????8?bytesSegment?Reserve:??????03f70000Segment?Commit:???????00002000DeCommit?Block?Thres:?00000800DeCommit?Total?Thres:?00002000Total?Free?Size:??????0002cd56Max.?Allocation?Size:?7ffdefffLock?Variable?at:?????06e90258Next?TagIndex:????????0000Maximum?TagIndex:?????0000Tag?Entries:??????????00000000PsuedoTag?Entries:????00000000Virtual?Alloc?List:???06e9009c06ea4000:?00200000?[commited?201000,?unused?1000]?-?busy?(b)070b2000:?00200000?[commited?201000,?unused?1000]?-?busy?(b)079f4000:?00200000?[commited?201000,?unused?1000]?-?busy?(b)07c0f000:?00200000?[commited?201000,?unused?1000]?-?busy?(b)0802b000:?00200000?[commited?201000,?unused?1000]?-?busy?(b)08238000:?00200000?[commited?201000,?unused?1000]?-?busy?(b)08444000:?00200000?[commited?201000,?unused?1000]?-?busy?(b)0865f000:?00200000?[commited?201000,?unused?1000]?-?busy?(b)0e20f000:?00200000?[commited?201000,?unused?1000]?-?busy?(b)0e42b000:?00200000?[commited?201000,?unused?1000]?-?busy?(b)0e635000:?00200000?[commited?201000,?unused?1000]?-?busy?(b)0e841000:?00200000?[commited?201000,?unused?1000]?-?busy?(b)0c661000:?00200000?[commited?201000,?unused?1000]?-?busy?(b)0c87e000:?00200000?[commited?201000,?unused?1000]?-?busy?(b)0ca8b000:?00200000?[commited?201000,?unused?1000]?-?busy?(b)0ea56000:?00200000?[commited?201000,?unused?1000]?-?busy?(b)0f062000:?00200000?[commited?201000,?unused?1000]?-?busy?(b)0f275000:?00200000?[commited?201000,?unused?1000]?-?busy?(b)...

從卦中可以看到大量的 commited 201000, unused 1000 ,這里的 0x201000 轉換一下大概就是 2M,以經驗來說,這 2M 大概就是 pdf,image,bitmap 等這些玩意了,由于沒有開啟 pageheap 或 ust,沒法追蹤到底是什么東西分配的,到這里就沒法進展下去了。

2. 到底是誰分配的 2M 數據

首先能進入 VirtualAllocdBlocks 數組自然是高層調用了 HeapAlloc 這類API,同時這個數據量高度懷疑是 Bitmap,Pdf 之類的大文件,很大可能是托管代碼做了什么導致這個資源沒有釋放,接下來使用 !dumpheap -stat 看下托管堆。

0:000>?!dumpheap?-stat
Statistics:MT????Count????TotalSize?Class?Name
...
09ae7e48??????627????????15048?System.Drawing.Bitmap
6b267040??????178???????366680?System.Decimal[]
6b2cb4a0?????1850???????601588?System.String[]
6b2cdd14?????1379???????638190?System.Byte[]
6b2cac14????15919??????1146764?System.String
09aec720????66332??????1326640?System.Drawing.FontFamily
09ae8590????66074??????2907256?System.Drawing.Font
Total?289300?objects

從卦中看,System.Drawing.Font 居然高達 6w 個,而且 System.Drawing.Bitmap 和 heap 上的 624 也非常接近,看樣子就是 Bitmap 啦,那為什么這個 Bitmap 沒有善終呢?可以用 !frq -stat 觀察下終結器隊列的 Freachable Queue 情況。

0:000>?!frq?-stat
Freachable?Queue:Count??????Total?Size???Type
---------------------------------------------------------152????????????3648???System.Data.Odbc.CNativeBuffer76????????????2128???System.Data.Odbc.OdbcConnectionHandle77????????????1540???System.Transactions.SafeIUnknown76????????????1824???System.Data.Odbc.OdbcStatementHandle2432??????????145920???System.Windows.Forms.Control+ControlNativeWindow304????????????7296???System.Drawing.Bitmap66062?????????2906728???System.Drawing.Font258????????????5160???System.Drawing.FontFamily308????????????9856???System.Drawing.Graphics308????????????3696???System.Windows.Forms.ImageList+NativeImageList1??????????????12???System.Drawing.Text.InstalledFontCollection12?????????????240???System.Threading.ThreadPoolWorkQueueThreadLocals1??????????????20???System.Security.Cryptography.SafeKeyHandle1??????????????20???Microsoft.Win32.SafeHandles.SafeWaitHandle6?????????????120???Microsoft.Win32.SafeHandles.SafeRegistryHandle12?????????????624???System.Threading.Thread1577???????????69388???System.Threading.ReaderWriterLock1??????????????20???System.Security.Cryptography.SafeProvHandle71,664?objects,?3,158,240?bytes

我去,這個可終結隊列居然高達 7.1w ,這是很有問題的,大概率當前的終結器線程瓦特了,接下來追查下 終結器線程 此時正在做什么 ?

0:000>?!t
ThreadCount:??????107
UnstartedThread:??0
BackgroundThread:?93
PendingThread:????0
DeadThread:???????12
Hosted?Runtime:???noLock??ID?OSID?ThreadOBJ????State?GC?Mode?????GC?Alloc?Context??Domain???Count?Apt?Exception0????1?138ac?0079fef0?????26020?Preemptive??00000000:00000000?00798f38?1?????STA?2????2?12b08?007adac0?????2b220?Preemptive??00000000:00000000?00798f38?0?????MTA?(Finalizer)?...0:000>?~2s
eax=00000001?ebx=00000000?ecx=00000000?edx=00000000?esi=00000001?edi=00000001
eip=777b2f8c?esp=0466eaf4?ebp=0466ec84?iopl=0?????????nv?up?ei?pl?nz?na?po?nc
cs=0023??ss=002b??ds=002b??es=002b??fs=0053??gs=002b?????????????efl=00000202
ntdll!NtWaitForMultipleObjects+0xc:
777b2f8c?c21400??????????ret?????14h
0:002>?k#?ChildEBP?RetAddr??????
00?0466ec84?762fc753?????ntdll!NtWaitForMultipleObjects+0xc
01?0466ec84?7695d9aa?????KERNELBASE!WaitForMultipleObjectsEx+0x103
02?0466ed34?7695c564?????combase!MTAThreadWaitForCall+0x1ca?[onecore\com\combase\dcomrem\channelb.cxx?@?7234]?
03?0466edc0?769a9923?????combase!MTAThreadDispatchCrossApartmentCall+0xf4?[onecore\com\combase\dcomrem\chancont.cxx?@?229]?
04?(Inline)?--------?????combase!CSyncClientCall::SwitchAptAndDispatchCall+0x9e4?[onecore\com\combase\dcomrem\channelb.cxx?@?5856]?
05?0466efa0?769ab739?????combase!CSyncClientCall::SendReceive2+0xad3?[onecore\com\combase\dcomrem\channelb.cxx?@?5459]?
06?(Inline)?--------?????combase!SyncClientCallRetryContext::SendReceiveWithRetry+0x29?[onecore\com\combase\dcomrem\callctrl.cxx?@?1542]?
07?(Inline)?--------?????combase!CSyncClientCall::SendReceiveInRetryContext+0x29?[onecore\com\combase\dcomrem\callctrl.cxx?@?565]?
...

從上面的 MTAThreadDispatchCrossApartmentCall 可知,這又是一個經典的 COM 釋放問題導致終結器線程卡死。。。接下來仔細看下 線程列表的 STA 情況,可以發現有大量的線程是 STA 模式。

d3c8587fb13b48d8805cf90aa4f592cd.png

接下來就是將結果告訴朋友,為什么開的線程都是 STA 套件模式。

三:總結

總的來說,這次內存泄漏的原因在于朋友開了 STA 模式的線程,導致終結器線程卡死,進而導致 Bitmap 分配之后無法釋放,最終引發非托管泄漏。

這個dump告訴我們,不要放棄,一定可以在絕望中找到希望。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/282806.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/282806.shtml
英文地址,請注明出處:http://en.pswp.cn/news/282806.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

android靜態方法如何測試,android – 如何使用mock()和spy()測試靜態方法

通常情況下,如果你最終使用PowerMock,這是一個很好的跡象,表明你最有可能是錯誤的方式.如果不是直接引用畢加索,而是創建一個組件,它的職責是加載圖像,讓我們說類ImageLoader.這會給你什么&#xff1f;>關注點分離&#xff1a;如果明天你決定轉移到Glide,你不應該改變你使用…

mysql經典的8小時問題-wait_timeout

2019獨角獸企業重金招聘Python工程師標準>>> 前段時間 現網突然頻繁報出 連接不上數據庫&#xff0c;偶滴的妖孽&#xff0c;其他地方都是用mysql&#xff0c;也沒遇到這個問題呀。 java.io.EOFExceptionat at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1913…

Chrome DevTools — Network

記錄網絡請求 默認情況下&#xff0c;只要DevTools在開啟狀態&#xff0c;DevTools會記錄所有的網絡請求&#xff0c;當然&#xff0c;記錄都是在Network面板展示的。 停止記錄網絡請求 點擊Stop recording network log紅色圖標&#xff0c;當它變為灰色時&#xff0c;表示DevT…

Blazor University 中文版網站已上線

在學習 Blazor 的過程中&#xff0c;找到了一個網站 Blazor University&#xff08;https://blazor-university.com&#xff09;。發現網站內容非常詳實&#xff0c;正像首頁所說的&#xff1a;通過瀏覽本網站中的信息&#xff0c;我打算帶您從完全的新手到Blazor的所有方面的專…

android:paddingtop 百分比,相對層中的百分比寬度

相對層中的百分比寬度我正在為登錄進行表單布局。Activity在我的Android應用程序中。下面的圖片是我希望它看起來的樣子&#xff1a;我能夠通過以下方式實現這個布局XML..問題是&#xff0c;這有點麻煩。我不得不對主機EditText的寬度進行硬編碼。具體而言&#xff0c;我必須具…

MySQL 查看表結構簡單命令

一、簡單描述表結構&#xff0c;字段類型 desc tabl_name; 顯示表結構&#xff0c;字段類型&#xff0c;主鍵&#xff0c;是否為空等屬性&#xff0c;但不顯示外鍵。 例如&#xff1a;desc table_name 二、查詢表中列的注釋信息 select * from information_schema.columns wher…

簡單獲取任意app的URL Schemes

簡單說明 最近業務需要&#xff0c;一直在查詢App的scheme相關信息&#xff0c;找到一種比較可靠的方法&#xff0c;分享給大家 步驟如下&#xff1a; 在電腦上使用iTunes下載那個app下載完后&#xff0c;在itunes里點擊這個app&#xff0c;選擇->Show in Finder&#xff0c…

Java中short、int、long、float、double的取值范圍

一、基本數據類型的特點&#xff0c;位數&#xff0c;最大值和最小值。1、基本類型&#xff1a;short 二進制位數&#xff1a;16 包裝類&#xff1a;java.lang.Short 最小值&#xff1a;Short.MIN_VALUE-32768 &#xff08;-2的15此方&#xff09;最大值&#xff1a;Short.MAX_…

.Net之接口文檔精度丟失處理

目的最近兩天在給朋友講解如何使用ajax調用接口時候&#xff0c;我發現我用swagger調用接口返回的long類型的數據最后幾位都變成了0(例如&#xff1a;6974150586715898000)&#xff0c;本來是以為sqlite數據庫不支持long類型導致我存進去的數據出了問題&#xff0c;然后我使用接…

android 訪問sqlite,android中訪問已有的sqlite數據庫

推薦文章每天進步記錄一點點話說經常性的操作svn出現各種問題,而度娘一直幫倒忙,是不是很手足無措.有時問題還是要記錄下來的.說不定還會有驚喜. 昨天遇到個問題,搜索了一下,發現第一條就是自己寫的.驚呆我了,更驚呆我的是,我是在csdn寫的,為什么在別的網站看到,完全一模一樣..…

Dnslog在SQL注入中的利用

參考文獻&#xff1a;www.anquanke.com/post/id/98096https://bbs.pediy.com/thread-223881.htm DNSlog在Web攻擊的利用 在某些無法直接利用漏洞獲得回顯的情況下&#xff0c;但是目標可以發起DNS請求&#xff0c;這個時候就可以通過DNSlog把想獲得的數據外帶出來。 常用情況 S…

讓泛型的思維扎根在腦海——深刻理解泛型

1.前言往往一些剛接觸C#編程的初學者&#xff0c;對于泛型的認識就是直接跳到對泛型集合的使用上&#xff0c;雖然微軟為我們提供了很多內置的泛型類型&#xff0c;但是如果我們只是片面的了解調用方式&#xff0c;這會導致我們對泛型盲目的使用。至于為什么要使用泛型&#xf…

fgetcsv()函數

fgetcsv()函數。fgetcsv()函數可以讀取指定文件的當前行&#xff0c;使用CSV格式解析出字段&#xff0c;并返回一個包含這些字段的數組。語法格式如下&#xff1a;array fgetcsv(resource $handle [, int $length [, string $delimiter [, string $enclosure[,string $escape]]…

android 系統ui修改器,分享兩個效果 - Android 系統 UI 管理

SystemUIManage.gifDimming the System Bars (沉浸模式)知乎 和 Medium 中都使用到了這個效果&#xff0c;作為沉浸式閱讀模式。// This example uses decor view, but you can use any visible view.View decorView getWindow().getDecorView();int uiOptions View.SYSTEM_U…

打游戲要存進度-備忘錄模式

打游戲要存進度-備忘錄模式 學習自 《大話設計模式》 備忘錄模式漫談 備忘錄的這種設計思想是非常常見的&#xff0c;比如說圍棋游戲的悔棋&#xff0c;繪圖軟件的撤銷功能等等&#xff0c;都或多或少的使用了備忘錄模式來處理對象的狀態。 備忘錄(Memento): 在不破壞封裝性的前…

(10.1)Python學習筆記二

1、在項目工程中要模塊化測試一個開發的功能&#xff0c;在測試通過后交付給項目組其他人員繼續開發。要保證代碼開發的性能和效率以及可擴展性。 2、項目工程中的文件夾分類要功能模塊明確清晰&#xff0c;在python中引入某一個 文件夾下的文件可以使用如下方式&#xff1a; t…

利用lay-ui結合ajax實現分頁功能(不借助框架,簡單易懂)

效果圖: 1.創建html頁面 01.html(前臺文件) 2.創建index.php(后臺文件) ------------------熱身結束,開始正式分頁之旅------------------ 3.在html頁面中引入layui需要用到的css以及js,還有我們自己額外需要用到的jquery 4.在html文件中,將基本的分頁欄顯示出來 5.好啦,htm…

Linux系統壓縮及解壓縮

Linux系統解壓縮概述&#xff1a;本篇將介紹Linux系統中的壓縮和解壓縮的工具&#xff0c;以及歸檔工具&#xff08;tar&#xff0c;cpio&#xff09;compress/uncompress&#xff1a;對應 .Z 結尾的壓縮格式文件&#xff1b;gzip/gunzip&#xff1a;其對應的是 .gz 結尾的壓縮…

酷派手機android版本,系統版本迎來升級

系統版本迎來升級這個應該是兩個版本之間最大但是卻不那么直觀的不同了&#xff0c;因為從TD版酷派大神F1采用的CoolLife UI 5.0版本&#xff0c;再到聯通版酷派大神F1所搭載的CoolLife UI 5.5版本&#xff0c;它們之間經歷了一個比較不錯的升級。在圖標ICON&#xff0c;功能設…

學習RUNOOB.COM進度一

了解MongoDB 由C語言編寫的&#xff0c;是一個基于分布式文件存儲的開源數據庫系統。在高負載的情況下&#xff0c;添加更多的節點&#xff0c;可以保證服務器性能。 特點 面向文檔&#xff0c;操作簡單容易 設置任何索引&#xff0c;實現更快排序 本地或者網絡創建數據鏡像&am…