c#应用程序中托管堆和本机堆之间有什么区别

时间:2015-06-09 04:40:56

标签: c#

从此http://blogs.msdn.com/b/visualstudioalm/archive/2014/04/02/diagnosing-memory-issues-with-the-new-memory-usage-tool-in-visual-studio.aspx

  1. 托管:对于托管应用程序,概要分析程序仅默认收集托管堆信息。托管堆分析是通过在分析器中捕获一组CLR ETW事件来完成的。
  2. Native:对于本机应用程序,探查器仅收集本机堆信息。为了收集本机堆信息,我们启用了堆栈跟踪和堆跟踪(ETW)的集合,它们非常详细,并将创建大型diagsession文件。
  3. 我的问题是在我的c#程序中(我只有c#代码和xaml文件)什么类型的对象将进入托管堆,什么类型的本机堆?如何在应用程序运行时指定每个堆的最大大小?我假设GC只在托管堆上运行,这是正确的吗?

1 个答案:

答案 0 :(得分:17)

当您使用C#中的new运算符(或任何其他CLR语言中的相应运算符)创建对象时,.NET运行时在“托管堆”中分配内存(只是由.NET运行时管理的堆+垃圾收集器)。实际上,这是两个堆中的一个 - 一个用于小于85K的对象,另一个用于大于此的对象(大型阵列等)。无论哪种方式,当分配这样的对象时,您不会像在本机代码中那样返回描述已分配空间地址的真实指针。你得到的是一个“句柄”,它表示该内存地址的间接。这种间接存在是因为GC收集并压缩堆时实际的内存位置可能会发生变化。

但是,如果要与需要指针的非托管/本机代码进行通信,则需要使用指针,而不是句柄。 .NET提供了两种方法将.NET句柄转换为可以传入非托管代码的原始指针。

  1. 使用Marshal.AllocHGlobalMarshal.AllocCoTaskMem在NT(或本机或非托管)堆上分配内存,并使用固定块或IntPtr.ToInt32 / ToInt64来获取基础指针。请确保您自己调用Marshal.FreeHGlobal / Marshal.FreeCoTaskMem或者非托管代码正确释放内存(Windows上为FreeAlloc / CoTaskMemFree)。
  2. 如果您的数据是blittable(在与本机代码互操作时可能就是这种情况),那么您只需使用GCHandle.Alloc将此托管数据固定,然后使用获取的原始指针调用本机代码来自GCHandle.AddrOfPinnedObject,完成后释放固定(使用GCHandle.Free)。您还可以获取托管对象的实际底层指针地址,并暂时插入C#中的“fixed”块内。
  3. 我希望这有帮助!