【问题标题】:`fixed` vs GCHandle.Alloc(obj, GCHandleType.Pinned)`fixed` vs GCHandle.Alloc(obj, GCHandleType.Pinned)
【发布时间】:2014-03-05 14:45:45
【问题描述】:

我试图找出用fixed 关键字定义的固定指针是如何工作的。我的想法是在内部使用GCHandle.Alloc(object, GCHandleType.Pinned)。但是当我查看为以下 C# 代码生成的 IL 时:

unsafe static void f1()
{
  var arr = new MyObject[10];
  fixed(MyObject * aptr = &arr[0])
  {
     Console.WriteLine(*aptr);
  }
}

我找不到任何GCHandle 的踪迹。

我看到在方法中使用了固定指针的唯一提示是以下 IL 声明:

.locals init ([0] valuetype TestPointerPinning.MyObject[] arr,
              [1] valuetype TestPointerPinning.MyObject& pinned aptr)

所以指针被声明为固定,并且不需要任何额外的方法调用来固定它。

我的问题是

  • 在声明中使用固定指针和使用GCHandle 类固定指针有什么区别吗?
  • 有什么方法可以在 C# 中声明一个固定指针而不使用fixed 关键字?我需要这个在循环中固定一堆指针,我无法使用固定关键字来做到这一点。

【问题讨论】:

    标签: c#


    【解决方案1】:

    嗯,肯定有区别,你看到了。 CLR 支持不止一种固定对象的方法。只有 GCHandleType.Pinned 方法直接暴露给用户代码。但是还有其他一些功能,例如"async pinned handles",该功能可以在驱动程序执行重叠 I/O 操作时保持 I/O 缓冲区固定。 fixed 关键字使用的那个,它根本不使用显式句柄或方法调用。添加了这些额外的方法是为了尽可能快速可靠地再次取消固定对象,这对 GC 运行状况非常重要。

    固定缓冲引脚由抖动实现。它在将 MSIL 转换为机器代码时执行两个重要的工作,高度可见的一个是机器代码本身,您可以使用调试器轻松查看它。但它也会生成垃圾收集器使用的数据结构,在调试器中完全不可见。 GC 要求可靠地找到存储在堆栈帧或 CPU 寄存器中的对象引用。在this answer 中详细了解该数据结构。

    抖动使用元数据中变量声明上的 [pinned] 属性在该数据结构中设置一个位,指示该变量引用的对象被暂时固定。 GC 看到这一点并且知道不移动对象。非常高效,因为它不需要显式调用方法来分配句柄,也不需要任何存储空间。

    但是不,这些技巧不适用于 C# 代码,您确实确实需要在代码中使用 fixed 关键字。或 GCHandle.Alloc()。如果您发现自己迷失在引脚中,那么您应该考虑 pinvoke 或 C++/CLI 的可能性很高,这样您就可以轻松调用本机代码。 pinvoke marshaller 用来在本机代码运行时保持对象稳定的临时 pin 是另一个不需要显式代码的自动 pinning 示例。

    【讨论】:

    • 那么,fixed 的性能是否比GCHandle.Alloc(xxx, GCHandleType.Pinned) 更好?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-07
    • 2019-10-07
    • 2015-05-08
    • 2011-11-04
    • 1970-01-01
    • 2013-01-15
    • 1970-01-01
    相关资源
    最近更新 更多