GCHandle 是 .NET 运行时提供的一种机制,用于在托管代码和非托管代码之间建立桥梁,控制垃圾回收器对对象的处理方式。它允许你固定对象在内存中的位置,防止垃圾回收器移动或回收该对象。
是的,你可以从对象中获取固定对象的 GCHandle。在 .NET 中,这通过 GCHandle.Alloc
方法实现,使用 GCHandleType.Pinned
参数。
using System;
using System.Runtime.InteropServices;
public class GCHandleExample
{
public static void Main()
{
// 创建一个对象
byte[] buffer = new byte[1024];
// 获取固定 GCHandle
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try
{
// 获取对象的固定地址
IntPtr address = handle.AddrOfPinnedObject();
Console.WriteLine($"Pinned object address: 0x{address.ToInt64():X}");
// 可以通过地址进行非托管操作
}
finally
{
// 必须释放 GCHandle
if (handle.IsAllocated)
{
handle.Free();
}
}
}
}
GCHandleType
枚举提供了几种类型:
Normal
:普通引用,不固定对象Pinned
:固定对象在内存中的位置Weak
:弱引用,允许对象被回收WeakTrackResurrection
:弱引用,跟踪复活对象原因:GCHandle 是非托管资源,忘记释放会导致内存泄漏
解决:始终在 finally 块中释放,或使用 using
模式封装
public sealed class PinnedGCHandle : IDisposable
{
private GCHandle _handle;
public PinnedGCHandle(object value)
{
_handle = GCHandle.Alloc(value, GCHandleType.Pinned);
}
public IntPtr AddrOfPinnedObject() => _handle.AddrOfPinnedObject();
public void Dispose()
{
if (_handle.IsAllocated)
{
_handle.Free();
}
}
}
// 使用示例
using (var handle = new PinnedGCHandle(buffer))
{
IntPtr ptr = handle.AddrOfPinnedObject();
// 使用 ptr...
}
原因:某些类型(如字符串)包含内部指针,不能固定 解决:先复制到可固定类型(如字节数组)
string str = "example";
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(str);
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
原因:长期固定大对象会妨碍垃圾回收器优化堆 解决:尽量减少固定时间,只在必要时固定
通过合理使用 GCHandle,你可以在需要时有效地控制 .NET 对象的内存行为,同时确保与非托管代码的安全交互。