2010-10-13 85 views
9

我有點混亂,可能這個問題很愚蠢。非託管組件的內存管理通過CLR

哪裏分配給非託管組件的內存?

在我的.NET代碼,如果我發起的非託管組件,此組件將被加載和內存分配?

如何託管和非託管堆之間的CLR馬歇爾通話?

編輯

感謝您的回覆,但我要問的是說假設我做user32.dll中的dllimport的,這顯然是一個非託管的DLL和我稱之爲user32.dll中的一些功能,現在我的問題,CLR如何打電話給這個無人問津的dll?

回答

10

它開始很容易。 pinvoke編組首先調用LoadLibrary並傳遞您指定的DLL名稱,即DllImportAttribute.Value屬性。在你的情況下,user32.dll已經被加載,因爲它被.NET引導程序加載,其引用計數只會增加。但通常Windows加載程序會將DLL映射到進程的地址空間,因此可以調用導出的函數。

接下來是GetProcAddress的獲取函數的地址調用,該DllImportAttribute.EntryPoint財產。除非您使用ExactSpelling,否則編組人員會進行一些嘗試。像「foo」這樣的函數名稱可以通過幾種可能的方式進行測試,foo和fooW或fooA。 Win32的令人討厭的實現細節與Unicode和Ansi字符之間的區別有關。 CharSet屬性在這裏很重要。

現在我需要揮手,因爲它變得棘手。編組器構造一個堆棧框架,設置需要傳遞給導出函數的參數。這需要低級代碼,仔細排除在窺探之外。以表面價值來看,它執行的是Marshal類支持的在託管和非託管類型之間轉換的翻譯類型。 DllImportAttribute.CallingConvention屬性在這裏很重要,因爲它決定了需要放置哪個參數值,以便被調用的函數能夠正確讀取它。

接着,它設置了一個SEH異常處理程序,以便通過所謂的代碼引發硬件異常可以被捕獲並轉換爲託管異常。生成更常見的一個AccessViolationException。和別的。

接下來,推棧上的一個特殊的cookie,表明非託管代碼即將開始使用堆棧。這可以防止垃圾收集器陷入非託管堆棧幀,並將它在那裏找到的指針解釋爲託管對象引用。您可以在調試器的調用堆棧[Managed to Native Transition]中看到此cookie。

接下來,只是間接調用GetProcAddress()所發現的函數地址。這會讓非託管代碼運行。

調用之後,可能需要執行清理以釋放分配用於傳遞非託管參數的內存。返回值可能需要轉換回受管理的值。就是這樣,假設沒有什麼討厭的事情發生,繼續執行下一個託管代碼語句。

8

非託管內存分配來自進程堆。您負責分配/釋放內存,因爲它不會收集垃圾,因爲GC不知道這些對象。

+0

您可能希望創建一個圍繞非託管組件的包裝,並實現了'IDisposable'接口,更清潔的方式重新分配它。垃圾回收器調用此接口中定義的函數以防止「遺忘」清理。 – 2010-10-13 18:02:04

+1

您可以將其封裝在IDisposable中,但請確保您調用Dispose或覆蓋終結器。僅僅實現IDispose是不夠的,GC不會調用Dispose,它會調用Object.Finalize(請參閱http://stackoverflow.com/questions/45036/will-the-gc-call-idisposable-dispose-for-me) – pstrjds 2010-10-13 18:04:31

0

您的問題的一部分由Michael回答。我回答了另一部分。

如果CLR加載到一個非託管過程中,它被稱爲CLR託管。這通常涉及在mscoree DLL中調用入口點,然後加載默認的AppDomain。在這種情況下,CLR會從進程中請求一塊內存,並且在給出時會成爲它的內存空間,並且會有堆棧和堆。

1

只是作爲一個學術一塊信息擴大什麼已經張貼在這裏:

有跡象表明,CLR使用約8個不同的堆:

  1. 裝載機堆:包含CLR結構和類型系統

  2. 高頻堆:靜,MethodTables,FieldDescs,接口映射

  3. 低Frequ ency堆:EEClass,類加載器和查找表

  4. 存根堆:存根CAS,COM包裝,P/Invoke的

  5. 大對象堆:需要超過85K字節

  6. GC內存分配堆:用戶分配的堆內存私有的應用

  7. JIT代碼堆:由mscoreee(執行引擎)分配的內存和JIT編譯器進行代碼託管

  8. 個工藝/基地堆:互操作/非託管的分配,本機內存等

HTH