C# 中的 GCHandle

CLR 给每个 AppDomain 都提供了一个 GC 句柄表,该表允许应用程序监视 C# 对象的生命周期或手动控制 C# 对象的生命周期。表中的每个条目都包含对托管堆上的对象的引用以及如何引用程序如何监视或控制该对象的标志。应用程序可以通过 GCHandle​ 对象在表中添加和删除条目,定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
namespace System.Runtime.InteropServices {
public struct GCHandle {
public bool IsAllocated {get; }
public object Target {get;set; }

public static GCHandleAlloc(objectvalue);
public static GCHandleAlloc(objectvalue, GCHandleType type);
public static GCHandleFromIntPtr(IntPtrvalue);
public static IntPtrToIntPtr(GCHandlevalue);
public IntPtrAddrOfPinnedObject();
public override boolEquals(object o);
public voidFree();
public override intGetHashCode();

public static bool operator ==(GCHandle a, GCHandle b);
public static bool operator !=(GCHandle a, GCHandle b);

public static explicit operator GCHandle(IntPtrvalue);
public static explicit operator IntPtr(GCHandlevalue);
}
}

要监视或控制对象的生命周期,可以调用 GCHandle.Alloc(object value, GCHandleType type)​ 方法,其中 GCHandleType​ 是一个枚举类型,定义如下:

1
2
3
4
5
6
7
8
namespace System.Runtime.InteropServices {
public enum GCHandleType {
Weak = 0,
WeakTrackResurrection = 1,
Normal = 2,
Pinned = 3
}
}

枚举值的含义如下:

Weak:此值允许应用程序监视对象的生命周期。可以用来判断垃圾收集器是否已经确认了该对象无法到达、将会或已被垃圾回收。对象的 Finalize​ 方法可能已执行也可能未执行,因此对象可能仍在内存中存在。

WeakTrackResurrection:此值允许应用程序监视对象的生命周期。和 Weak 的作用基本一致,区别就是:对象的 Finalize​ 方法(如果存在)肯定已执行,并且该对象的内存已被回收。

Normal:此值允许应用程序控制对象的生命周期。通过设定该值,应用程序通知垃圾收集器该对象必须保留在内存中无法被回收,即使此对象已无法到达。垃圾收集器执行 GC 时可以压缩或移动该对象的内存,是 GCHandle.Alloc(object value)​ 的默认值。

Pinned:此值允许应用程序控制对象的生命周期。通过设定该值,应用程序通知垃圾收集器该对象必须保留在内存中无法被回收,即使此对象已无法到达,而且也不能在 GC 时压缩或移动该对象的内存。常用的应用场景是将一个 C# 中的托管对象作为指针进传递 C++ 函数的原生代码时,为了避免传递进去的对象指针失效,会设定此值。