C#中赋值为null的含IntPtr对象是否会被GC回收并释放C++分配内存?
问题背景
你当前使用的是C++编写的自内存管理DLL,DLL对外提供Init(ref IntPtr connectHandle)接口申请非托管内存、Close(IntPtr connectHandle)接口释放对应非托管内存。你编写的C#代码如下:
class Test { public IntPtr _connectHandle; // 构造函数 public Test() { // 调用C++侧Init接口,原代码此处笔误应为ref _connectHandle cPlusPlus_Init(ref _connectHandle); } public void Close() { // 调用C++侧Close接口 cPlusPlus_Close(_connectHandle); } // 终结器 ~Test() { Close(); } }
问题解答
问题1:将不再使用的b赋值为null后,b包含的IntPtr类型成员会被GC回收吗?
- 会被GC回收,但要明确回收的对象范围:
IntPtr是托管侧的值类型,本质是用来存储内存地址的数值,作为Test类的实例成员,它占用的内存属于托管堆内存。当你将b赋值为null后,b原来指向的Test实例就失去了所有根引用,GC扫描到无引用的托管对象后,会将整个Test实例占用的托管内存回收,其中自然包含_connectHandle这个IntPtr变量占用的托管内存。
问题2:如果该对象可被GC回收,GC会同时释放C++侧申请的内存吗?
- 绝对不会。C++侧申请的内存属于非托管内存,完全不受.NET GC的管理,GC没有能力也不会主动释放这部分内存。
- 这里还有一个非常容易踩的坑需要注意:你提到
Clone()是深拷贝,但如果你的Clone逻辑只是简单把a的_connectHandle数值复制给b的_connectHandle,那本质上是浅拷贝,两个Test实例会持有同一个指向C非托管内存的指针。当b被GC回收时,会触发终结器调用Close()释放对应的C内存,此时a持有的指针就变成野指针,后续a访问对应资源时会直接触发访问异常。如果要做真正的深拷贝,你需要在Clone时给新的Test实例调用C++的Init接口申请独立的非托管内存,而不是直接复制指针。
额外优化建议
你当前的Test类实现存在非托管资源释放的风险:.NET的终结器执行时机是不确定的,且仅在GC回收时才会触发,会导致非托管资源占用时间不可控。建议你实现IDisposable接口,提供主动释放非托管资源的能力,避免资源泄漏和野指针问题。
内容的提问来源于stack exchange,提问作者jzian
相关产品推荐
相关产品推荐

