【问题标题】:Memory Management Of Unmanaged Component By CLRCLR 对非托管组件的内存管理
【发布时间】:2011-04-25 00:00:04
【问题描述】:

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

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

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

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

编辑

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

【问题讨论】:

    标签: c#


    【解决方案1】:

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

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

    现在我需要稍微挥手,因为这很棘手。编组器构造一个堆栈帧,设置需要传递给导出函数的参数。这需要低级代码,小心地从窥探中排除。从表面上看,它执行 Marshal 类支持在托管和非托管类型之间转换的那种转换。 DllImportAttribute.CallingConvention 属性在这里很重要,因为它决定了需要放置什么参数值,以便被调用的函数可以正确读取它。

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

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

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

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

    【讨论】:

      【解决方案2】:

      非托管内存分配来自进程堆。您负责分配/解除分配内存,因为 GC 不知道这些对象,所以它不会被垃圾回收。

      【讨论】:

      • 您可能希望围绕非托管组件创建一个包装器,并实现IDisposable 接口,以便以更简洁的方式释放它。垃圾收集器调用该接口中定义的函数,防止“忘记”清理。
      • 您可以将它包装在 IDisposable 中,但请确保调用 Dispose 或覆盖终结器。仅仅实现IDispose是不够的,GC不调用Dispose,它调用Object.Finalize(见stackoverflow.com/questions/45036/…
      【解决方案3】:

      就像一篇扩展此处发布内容的学术信息:

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

      1. 加载程序堆:包含 CLR 结构和类型系统

      2. 高频堆:静态、MethodTables、FieldDesc、接口映射

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

      4. 存根堆:用于 CAS、COM 包装器、P/Invoke 的存根

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

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

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

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

      HTH

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-09-24
          • 1970-01-01
          • 2011-11-23
          • 2011-09-01
          • 2013-12-14
          • 2013-03-14
          • 2013-10-28
          • 2012-01-30
          相关资源
          最近更新 更多