GCHandle.Alloc是否分配内存?

时间:2015-03-11 09:24:52

标签: c# garbage-collection

我正在使用SciTech的.NET Memory Profiler来降低程序的内存分配率并减少垃圾收集的频率。

令人惊讶的是,根据分析器,最大量的分配似乎来自GCHandle.Alloc调用我正在做的将现有的.NET数组编组为本机OpenGL。

我的理解是,调用GCHandle.Alloc不分配内存,它只引导托管堆上的现有内存?

我错了还是个人资料错了?

3 个答案:

答案 0 :(得分:12)

.NET reference source可供所有人查看,您可以查看并自行查找。

如果你深入研究GCHandle.Alloc,你会看到它调用一个名为InternalAlloc的原生方法:

[System.Security.SecurityCritical]  // auto-generated
[MethodImplAttribute(MethodImplOptions.InternalCall)]
[ResourceExposure(ResourceScope.None)]
internal static extern IntPtr InternalAlloc(Object value, GCHandleType type);

向下钻取到CLR代码,您会看到对MarshalNative::InternalAlloc的内部呼叫,该呼叫最终会调用:

hnd = GetAppDomain()->CreateTypedHandle(objRef, type);

反过来调用ObjectHandle::CreateTypedHandle - > HandleTable::HndCreateHandle - > HandleTableCache->TableAllocSingleHandleFromCache如果缓存中不存在句柄,则分配句柄。

正如@Antosha纠正我的那样,调用的地方不是通过ComDelegate(实际上很少发生),而是通过MarshalNative。分配确实发生,而不是在托管堆上,而是由运行时保留的外部堆,用于管理GC对象的句柄根。 在托管堆中发生的唯一分配是IntPtr,它保存指向表中地址的指针。尽管如此,您仍应确保在完成后致电GCHandle.Free

答案 1 :(得分:1)

  

探查器甚至为我分配的每个GCHandle分配特定的内存量--8个字节。并且每个GCHandle.Alloc的托管堆似乎增长了8个字节。所以它似乎确实在托管堆上分配空间,虽然我不知道为什么?

我不知道手柄有多小:)我做了一些测试:

Console.WriteLine("Is 64 bit: {0}, IntPtr.Size: {1}", Environment.Is64BitProcess, IntPtr.Size);

int[][] objects = new int[100000][];
for (int i = 0; i < objects.Length; i++)
{
    objects[i] = new int[] { 0 };
}

long w1 = Environment.WorkingSet;

GCHandle[] handles = new GCHandle[objects.Length];

for (int i = 0; i < handles.Length; i++)
{
    //handles[i] = new GCHandle(handles);
    //handles[i] = GCHandle.Alloc(objects[i]);
    handles[i] = GCHandle.Alloc(objects[i], GCHandleType.Pinned);
}

Console.WriteLine("Allocated");
long w2 = Environment.WorkingSet;
Console.WriteLine("Used: {0}, by handle: {1}", w2 - w1, ((double)(w2 - w1)) / handles.Length);
Console.ReadKey();

这是一个小程序。如果您运行,您会看到“空”GCHandle(使用new GCHandle()创建的一个)占用IntPtr.Size内存。如果您使用ILSpy来查看它,则很清楚:它有一个IntPtr字段。如果你固定某个对象,那么它会占用2*IntPtr.Size个内存。这可能是因为它必须在CLR表(大小为IntPtr)及其内部IntPtr

中写入内容

取自https://stackoverflow.com/a/18122621/613130

  

它使用CLR内置的GC句柄专用表。您使用GCHandle.Alloc()为此表分配一个条目,稍后再使用GCHandle.Free()将其释放。垃圾收集器只是将此表中的条目添加到它在执行集合时发现的对象的图形。

答案 2 :(得分:-1)

来自msdn

  

保护对象免受垃圾回收的新GCHandle。这个GCHandle必须在不再需要时免费发布。

因此,即使没有进行实际分配,我想在调用Free之前不会发布预分配版本。