除了保持对象的引用之外,GCHandle.Alloc是否做了什么?

时间:2013-08-08 04:16:24

标签: c# interop

GCHandle.Alloc“保护对象免受垃圾收集”,但仅仅在静态变量中保存对该对象的引用也会阻止它被收集。 GCHandle.Alloc提供了哪些好处(假设GCHandleType.Normal)?

This article表示代理“无需在任何特定的内存位置修复”,但我找不到MSDN上的任何文档来支持该语句。如果委托由CLR垃圾收集器移动,那么umanaged库如何找到它以便可以调用它?

请注意,代理不能固定;你会得到一个例外,说明“对象包含非原始或非blittable数据”。

1 个答案:

答案 0 :(得分:8)

垃圾收集器通常通过遍历AppDomain静态以及正在运行的线程的堆栈以及它们在GC堆上引用的对象来发现托管对象。但是在某些情况下,收集器本身无法找到对已存在且不应收集的对象的引用。

当非托管代码使用此类对象时会发生这种情况。这段代码没有jit,所以GC没有很好的方法来发现对象引用,这样的代码的堆栈帧不能可靠地检查以找回指针。您必须确保GC仍然可以看到参考。这是GCHandle的作用。它使用CLR内置的GC手柄专用表。您使用GCHandle.Alloc()为此表分配一个条目,稍后使用GCHandle.Free()再次释放它。垃圾收集器只是将此表中的条目添加到它在执行集合时发现的对象的图形。

gcroot<> C ++ / CLI中的关键字就是一个例子。它允许编写可以存储简单原始指针的非托管代码,并在需要时将其转换回托管对象引用。 GCHandle.ToIntPtr()方法生成该指针,FromIntPtr()恢复对象引用。 GCHandle表条目确保在非托管代码显式调用Free()之前不会收集对象。通常由C ++析构函数完成。

GCHandle还支持固定对象的功能,当您需要编组本机代码并且pinvoke marshaller无法完成工作时使用该对象。您将传递GCHandle.AddrOfPinnedObject()的返回值。它实现了弱引用,尽管你通常总是使用WeakReference类。它实现了异步固定句柄,允许在I / O完成时自动取消固定固定I / O缓冲区,这种功能不会直接暴露。所以,是的,GCHandle所做的不仅仅是保持参考。

对于有关委托的问题很重要,CLR支持使用委托来调用本机代码。底层辅助函数是Marshal.GetFunctionPointerForDelegate()。它动态构建机器代码存根,允许本机代码回调到托管代码。这需要委托对象保持引用,GCHandle.Alloc()通常用于此。这不是唯一的方法,将委托存储到静态变量是另一种确保委托对象不会被垃圾收集的方法。该委托不需要固定,GCHandleType.Normal就好了。

这在任何.NET程序中都有很多用处,但大多数都不在视线范围内。特别是在.NET框架代码本身和pinvoke marshaller中。只有在本机代码存储函数指针并且可以稍后进行回调时,才必须使用GCHandle来保护委托对象。