从固定缓冲区获取的Span能否安全存活于GC压缩?
关于fixed数组创建Span的GC安全性问题
首先看示例代码:
public unsafe struct CharacterStorage { public const int Capacity = 20; fixed char Characters[Capacity]; public Span<char> GetSpan() => MemoryMarshal.CreateSpan(ref Characters[0], Capacity); }
从GetSpan()获取的Span能否在GC压缩后正常使用?
分两种场景判断:
- 当
CharacterStorage实例是栈分配时(比如在方法内声明局部变量):栈内存不受GC压缩机制影响,只要结构体实例还在栈上有效,Span就能正常使用。 - 当
CharacterStorage实例是堆分配时(比如作为引用类型的字段、被装箱,或存入堆上集合):GC压缩会移动包含该结构体的宿主对象,此时Span内部保存的旧内存地址会失效,访问Span会触发内存错误,无法正常使用。
原因是fixed数组是CharacterStorage结构体的一部分,堆上的结构体实例会随宿主对象一起被GC移动,但Span不会自动更新内部的指针引用,GC压缩后Span指向的是已经被回收或复用的内存区域。
文档未提及GC压缩,能否认为该操作具备GC安全性?
不能。MemoryMarshal.CreateSpan的文档虽然没直接提到GC压缩,但它强调的生命周期注意事项已经覆盖了这个场景:你必须保证Span的使用周期不超过其引用内存的有效生命周期。对于堆上的CharacterStorage实例,GC压缩会改变其内存地址,相当于原内存的有效生命周期在移动后结束,此时Span就不再安全。
这个操作不具备通用的GC安全性,使用时必须严格控制Span的生命周期,避免在结构体实例可能被GC移动的场景下持有Span。
内容的提问来源于stack exchange,提问作者Moe Baker
相关产品推荐
相关产品推荐

