调用GCHandle.Alloc固定类实例时,其成员是否同步固定?及外部类固定问询
问题解答
1. 固定Foo实例能否保证Bar和Baz成员不被GC回收?
不能。你调用的GCHandle.Alloc(foo, GCHandleType.Normal)创建的是普通强引用句柄,它仅能确保foo实例本身不会被GC回收,但不会自动处理foo的成员对象:
Bar和Baz是引用类型,它们的实例存储在堆上的独立位置,foo中仅保存指向它们的引用。- 只要
foo被该句柄持有(属于GC根),bar和baz作为foo的成员不会被回收,但它们的内存地址仍可能被GC移动(如果是移动型GC)。如果你的原生代码需要直接访问bar或baz的内存指针,仅固定foo完全不够——指针会因GC移动失效。
补充:即便使用GCHandleType.Pinned固定foo实例,也只是固定foo自身的内存地址,不会延伸到它的成员对象。
2. 显式固定非自研类的私有成员对象的方法
如果非自研类的私有成员是引用类型,你无法直接访问,但可以通过反射获取引用后创建句柄:
- 反射获取私有成员:利用
Type.GetField并指定BindingFlags.NonPublic | BindingFlags.Instance参数,拿到私有字段的引用对象,再为其创建GCHandle。示例代码:Foo foo = new Foo(); // 获取Foo类的私有bar字段 FieldInfo barField = typeof(Foo).GetField("bar", BindingFlags.NonPublic | BindingFlags.Instance); Bar barInstance = (Bar)barField.GetValue(foo); // 创建强引用句柄防止barInstance被回收,若需固定内存则用GCHandleType.Pinned GCHandle barHandle = GCHandle.Alloc(barInstance, GCHandleType.Normal); - 必须释放句柄:所有创建的
GCHandle在不再使用时必须调用GCHandle.Free(),否则会引发内存泄漏。 - 风险提示:反射访问私有成员会破坏类的封装性,若类的内部实现(如私有字段名称)变更,代码会直接失效,使用前需评估兼容性风险。
内容的提问来源于stack exchange,提问作者Shmoopy
相关产品推荐
相关产品推荐

