能否将MemoryHandle仅存储于非托管内存并防止底层托管对象被回收?
Memory固定后存储指针到非托管内存的解决方案
首先明确:你无法通过公开API从MemoryHandle获取内部的GCHandle,这是.NET刻意封装的内部实现细节,强行反射获取会导致代码不稳定,绝对不推荐。不过我们可以用手动管理GCHandle的方式解决你的问题。
可行方案:手动创建并管理GCHandle
既然不能依赖MemoryHandle的自动GC句柄管理,不如自己手动创建固定句柄,完全掌控生命周期:
1. 固定Memory并存储指针+GCHandle到非托管内存
public IntPtr PinAndStoreMemory<T>(Memory<T> memory) { // 先获取Memory<T>对应的托管对象(如果有的话) object? target = null; using (var tempHandle = memory.Pin()) { target = tempHandle.Target; } IntPtr pointer; GCHandle handle = default; if (target != null) { // 托管对象来源的Memory<T>:创建固定GCHandle防止GC回收 handle = GCHandle.Alloc(target, GCHandleType.Pinned); pointer = handle.AddrOfPinnedObject(); } else { // 非托管内存来源的Memory<T>:直接取指针即可 ref var pinnable = ref memory.Span.GetPinnableReference(); pointer = (IntPtr)Unsafe.AsPointer(ref pinnable); } // 将指针和GCHandle打包存储到非托管内存(示例用结构体,实际根据你的存储方式调整) var pinnedInfo = new PinnedMemoryInfo { Pointer = pointer, Handle = handle }; // 写入非托管内存的逻辑,比如用Marshal.AllocHGlobal分配后写入 // ... return pointer; } // 用于存储指针和GCHandle的结构体 struct PinnedMemoryInfo { public IntPtr Pointer; public GCHandle Handle; }
2. 使用完成后释放资源
当你不再需要这个指针时,从非托管内存取出PinnedMemoryInfo,释放GCHandle和非托管内存:
public void ReleasePinnedMemory(PinnedMemoryInfo pinnedInfo) { // 释放GCHandle,允许GC回收托管对象 if (pinnedInfo.Handle.IsAllocated) { pinnedInfo.Handle.Free(); } // 释放存储PinnedMemoryInfo的非托管内存(如果有分配的话) // ... }
关键注意点
- 区分Memory
的来源 :如果Memory是从非托管内存(比如NativeMemory.Alloc)创建的,不需要GCHandle,直接取指针就行,因为没有托管对象需要GC管理。 - 严格控制GCHandle生命周期:必须保证在指针使用期间,GCHandle一直处于分配状态;用完立刻释放,否则会造成托管内存泄漏。
- 线程安全:如果多线程操作同一个指针和GCHandle,一定要加同步锁,避免释放后继续使用指针导致崩溃。
为什么不能直接存MemoryHandle到非托管内存?
MemoryHandle是实现了IDisposable的托管对象,它的作用是在Dispose时自动释放内部的GC句柄。但如果你把它存到非托管内存,.NET的GC无法跟踪到这个托管实例,一旦MemoryHandle本身被GC回收,内部的GC句柄就会被释放,底层托管对象会被回收,指针就变成了悬空指针,直接导致程序崩溃。所以必须手动管理GCHandle,而不是依赖MemoryHandle的自动机制。
内容的提问来源于stack exchange,提问作者Tacodiva
相关产品推荐
相关产品推荐

