CLR对非托管组件的内存管理

时间:2010-10-13 17:55:14

标签: c#

我有点困惑,可能这个问题很傻。

为非托管组件分配的内存在哪里?

在我的.net代码中,如果我启动了一个非托管组件,这个组件将被加载并分配内存?

CLR如何在托管堆和非托管堆之间调用?

修改

感谢您的回复,但我要问的是假设我做了一个User32.Dll的DLLIMPORT,这显然是一个非托管的DLL,我现在调用User32.DLL中的一些函数我的问题,CLR如何编组我的调用unmanged dll?

4 个答案:

答案 0 :(得分:10)

它开始很简单。 pinvoke marshaller首先调用LoadLibrary并传递您指定的DLL名称DllImportAttribute.Value属性。在您的情况下,user32.dll已经加载,因为它由.NET引导程序加载,它的引用计数只是递增。但通常Windows加载器会将DLL映射到进程的地址空间,以便可以调用导出的函数。

接下来是GetProcAddress以获取要调用的函数的地址,即DllImportAttribute.EntryPoint属性。除非您使用ExactSpelling,否则编组器会进行几次尝试。像“foo”这样的函数名被测试了几种可能的方式,foo和fooW或fooA。令人讨厌的Win32实现细节与Unicode和Ansi字符之间的区别有关。 CharSet属性在这里很重要。

现在我需要挥手一点,因为它变得棘手。 marshaller构造一个堆栈框架,设置需要传递给导出函数的参数。这需要低级别的代码,小心地从窥探中排除。以它的面值来表示它执行Marshal类支持的托管和非托管类型之间的转换。 DllImportAttribute.CallingConvention属性在这里很重要,因为它确定需要放置哪个参数值,以便被调用函数可以正确读取它。

接下来,它设置了一个SEH异常处理程序,以便可以捕获被调用代码引发的硬件异常并将其转换为托管异常。生成更常见的一个,AccessViolationException。还有其他人。

接下来,它在堆栈上推送一个特殊的cookie来指示非托管代码即将开始使用堆栈。这可以防止垃圾收集器浮入非托管堆栈帧并将其在那里找到的指针解释为托管对象引用。您可以在调试器的调用堆栈中看到此cookie,[Managed to Native Transition]。

接下来,只是间接调用GetProcAddress()中的函数地址。这会使非托管代码运行。

调用之后,可能需要进行清理以释放分配用于传递非托管参数的内存。可能需要将返回值转换回托管值。就是这样,假设没有发生任何令人讨厌的事情,继续执行下一个托管代码语句。

答案 1 :(得分:8)

非托管内存分配来自进程堆。您负责分配/取消分配内存,因为它不会收集垃圾,因为GC不知道这些对象。

答案 2 :(得分:1)

正如一篇学术信息扩展到这里发布的内容:

CLR使用大约8种不同的堆:

  1. Loader Heap:包含CLR结构和类型系统

  2. 高频堆:静态,MethodTables,FieldDescs,界面图

  3. 低频堆:EEClass,ClassLoader和查找表

  4. Stub Heap:CAS,COM包装器的存根,P / Invoke

  5. 大对象堆:需要超过85k字节的内存分配

  6. GC堆:用户为应用分配了专用的堆内存

  7. JIT代码堆:由mscoreee(执行引擎)分配的内存和托管代码的JIT编译器

  8. 进程/基本堆:互操作/非托管分配,本机内存等
  9. HTH

答案 3 :(得分:0)

迈克尔回答了部分问题。我回答了另一部分。

如果CLR加载到非托管进程中,则称为CLR托管。这通常涉及调用mscoree DLL中的入口点,然后加载默认的AppDomain。在这种情况下,CLR会从进程中请求一块内存,当给定时,它将成为其内存空间并将具有堆栈和堆。