C#也需要内存管理(GC系统)

来源:互联网 发布:软件项目心得体会 编辑:程序博客网 时间:2024/05/16 08:48

大部分时候,我们不必关心GC,让它自己工作就可以了。但是在P/Invoke期间,为了编写稳定健壮的代码就需要对GC进行必要的控制。下面就来说说如何控制GC。

类库提供了GC,GCHandle,HandleRef,Marshal这四种类型来管理GC。其中GC和Marshal是静态类型,而GCHandle和HandleRef是值类型。

如果使用P/Invoke来调用托管对象,而且该对象在该调用之后不在其他地方引用,则垃圾回收器有可能终结该托管对象。此操作将释放资源并使句柄无效,从而导致P/Invoke失败。使用 HandleRef 包装句柄,可保证该托管对象在P/Invoke完成前不被垃圾回收。

HandleRef 值类型与 GCHandle 类似,是可由‘ interop 封送拆收器’识别的特殊类型。虽然普通的非固定 GCHandle 也可以防止垃圾回收时机不正确,但 HandleRef 可以提供更好的性能。尽管使用 HandleRef 是保持一个对象在P/Invoke期间处于活动状态的首选方案,但也可以使用 GC.KeepAlive() 方法达到相同的目的。

HandleRef 构造函数采用两个参数:Object 表示包装,IntPtr 表示非托管句柄。Interop 封送拆收器仅将句柄传递给非托管代码,并保证包装的对象(作为第一个参数传递给 HandleRef 的构造函数)在调用期间保持活动状态。

GC.KeepAlive()方法的目的是确保对对象的引用存在,该对象有被垃圾回收器过早回收的危险。这种现象可能发生的一种常见情形是,当在托管代码或数据中已没有对该对象的引用,但该对象仍然在非托管代码(如 Win32 API、非托管 DLL 或使用 COM 的方法)中使用。另一种过早发生垃圾回收的情形是,在一个方法中创建并使用一个对象。此时,当对对象的某个成员的调用仍在执行时,可能会对该对象进行回收。

GC.KeepAlive()方法引用 obj,从而使该对象从例程开始到调用此方法的那个位置(按执行顺序)均不符合进行垃圾回收的条件。在 obj 必须可用的指令范围的结尾(而不是开头)编写此方法的代码。

GC.KeepAlive()方法除了延长作为参数传递的对象的生存期之外,不会执行任何操作,也会不产生任何其他副作用。

GCHandle 类与 GCHandleType 枚举结合使用以创建对应于任何托管对象的句柄。此句柄可为以下四种类型之一:Weak、WeakTrackResurrection、Normal 或 Pinned。 分配了句柄以后,在非托管客户端保留唯一的引用时,可以使用它防止垃圾回收器回收托管对象。如果没有这样的句柄,则在该对象相关的非托管客户端完成工作以前,有可能被垃圾回收器回收。

还可以使用 GCHandle 创建一个固定对象,该对象返回一个内存地址,并防止垃圾回收器在内存中移动该对象,类似C#关键字fixed的功能。

当该句柄超出范围时,您必须通过调用 Free 方法显式释放它;否则,可能会发生内存泄漏。 当您释放固定的句柄时,如果没有对关联对象的其他引用,则关联对象将解除固定,并可以被当成垃圾回收。

在C#编程中,fixed关键字可以用于固定对象内存,也是GC控制的良方。

使用GCHandle得注意,当你不再使用它时,必须通过GCHandle.Free()来释放句柄!

 

原创粉丝点击