fixed块内执行实例创建是否会引发指针失效、内存损坏问题?
问题结论
你给出的代码逻辑本身符合C#语言规范,正常运行场景下fixed块钉选的数组不会被GC移动,指针p不会指向无效地址,也不会造成内存损坏。你观测到的偶发数据异常是**.NET Framework 4.0版本的GC已知缺陷**导致的。
根因分析
- 按照C#语言规范,
fixed块的钉选标记会在块的整个作用域内生效,被钉选的托管对象不会被GC重定位,相关指针的有效性受到 runtime 保障。 - .NET Framework 4.0的后台GC实现存在bug:当
fixed块钉选的小对象位于托管堆的代际临界点,且fixed块内部或其他并行线程的分配操作触发GC时,极端场景下后台GC会漏处理该钉选标记,导致数组对象被移动但fixed块内的指针未同步更新,最终指针指向的内存区域已经不属于原数组,就会出现读取到的数据后半段为0、取值异常的现象。 Marshal.PtrToStructure方法内部会创建目标类型的实例,属于堆分配操作,高频率调用时很容易触发GC,刚好命中了上述bug的触发条件。
不同.NET版本的表现
.NET Framework 4.0
存在上述后台GC钉选标记处理缺陷,高频率调用你给出的示例代码时会偶发数据异常,该问题已经被微软官方确认。
.NET Framework 4.7
该缺陷已经在.NET Framework 4.5及后续版本修复,你给出的示例代码在4.7环境下可以安全运行,不存在指针失效风险,也不会出现数据异常问题。
相关说明
你看到的「避免在fixed块内执行堆分配操作」属于通用性能优化建议:钉选对象会提升GC压缩阶段的开销,若fixed块执行周期长且内部频繁触发GC,会降低整体GC效率,但该建议本身不涉及正确性问题,只是刚好在.NET 4.0的bug场景下会触发异常。
你改用GCHandle显式钉选数组的方案可以兼容所有.NET Framework版本,完全规避上述缺陷。
内容的提问来源于stack exchange,提问作者Adam L. S.
相关产品推荐
相关产品推荐

