固定对象

GC (垃圾收集器)负责清理我们的垃圾。

GC 清理我们的垃圾时,他会从托管堆中删除未使用的对象,这会导致堆碎片。当 GC 完成删除时,它会执行堆压缩(defragmintation),这涉及在堆上移动对象。

由于 GC 不是确定性的,当将托管对象引用/指针传递给本机代码时, GC 可以随时启动,如果它恰好在 Inerop 调用之后发生,那么对象(传递给本机的引用)很可能会在托管堆上移动 - 结果,我们在托管端获得了无效的引用。

在这种情况下,你应该在将对象传递给本机代码之前固定该对象。

固定对象

固定对象是 GC 不允许移动的对象。

Gc 固定手柄

你可以使用 Gc.Alloc 方法创建一个 pin 对象

GCHandle handle = GCHandle.Alloc(yourObject, GCHandleType.Pinned); 
  • 获取固定 GCHandle 到托管对象会将特定对象标记为 GC 无法移动的对象,直到释放句柄为止

例:

[DllImport("kernel32.dll", SetLastError = true)]
public static extern void EnterCriticalSection(IntPtr ptr);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern void LeaveCriticalSection(IntPtr ptr);
       
public void EnterCriticalSection(CRITICAL_SECTION section)
{
    try
    {
        GCHandle handle = GCHandle.Alloc(section, GCHandleType.Pinned); 
        EnterCriticalSection(handle.AddrOfPinnedObject());
        //Do Some Critical Work
        LeaveCriticalSection(handle.AddrOfPinnedObject());
    }
    finaly
    {
        handle.Free()
    }
}

注意事项

  • 当固定(特别是大的)对象尝试尽可能快地释放固定的 GcHandle 时,因为它会中断堆碎片整理。
  • 如果你忘记释放 GcHandle 什么都不会。在安全的代码部分(例如最终)进行