c# 关于内存回收GC的简单总结

本文介绍了GC机制,其旨在占用少CPU时间确保对象有可用内存,通常在内存少、新对象急需内存时发生。还对比了C++和C#的析构函数,指出C#析构函数由GC调用,不适合释放非内存资源。此外,介绍了“Dispose设计方式”释放非内存资源,以及GC的一些重要事实。

GC

废弃对象的4种方式

方式例子被释放的对象
将null引用赋值给对象的仅剩引用。(赋空值)ClassA a = new ClassA();a=null;没有一个引用引用它时
将不同对象的引用赋值给对象的仅剩引用。(赋新值)a1 = new ClassA();a2 = new ClassA(); a1 = a2;a1的原先引用对象
包含对象A的仅剩引用的对象B被废弃。(主人被废弃)b = new ClassB();a = new ClassA(b);b = null;a随着b的引用对象废弃而废弃
方法调用完返回原作用域时,方法调用过程中的引用越出了作用域。(临时变量)void funa(){ ClassA a = new ClassA();}funa调用结束后a被废弃

为了避免占用大量CPU处理时间,GC占用尽量少的CPU时间来确保所有对象有足够的可用内存,且不定时,时间短(惰性)。通常在内存较少,新对象急需内存时发生。

可以使用静态方法System.GC.Collect()来建议GC立即执行。一般在废弃对象后获执行对GC造成的停滞敏感的程序部分。

除了内存之外,需要重视的是,对象也会占用非内存有限资源:文件、网络/数据库连接。GC是不会释放他们的(是啊,怎么可以等懒惰的GC来释放他们)。

在C++中,我们有析构函数。程序员可以在其中释放(内存/非内存)资源,而且一调用立即执行。(可别羡慕,虽然可操作空间大了,性能好了,但没有GC,还会有些坑:悬垂指针,内存泄漏)


Class ClassA(){
    ...
    ~ClassA(){
        //释放资源。
    } 
}

C#也有析构函数,但只有GC能调用,所以不能像C++那样好用,不适合用来释放非内存有限资源了。

另外,GC会立即回收没有析构函数的废弃对象,但会把带析构函数的废弃对象先放入一个特殊列表。然后结束突发性废弃对象判断后,再启动一个进程执行列表中每个对象的析构函数。(可能因为要优先释放没析构函数的快的对象给急需内存的燃眉之急吧,然后在不干扰当前进程任务的情况下,再切换个进程解决多事的有析构函数的对象)。然后还要再放入一个即将回收对象列表中,再等GC下一次突发动作后,才被回收。

也就是总共要两次GC突发动作,才能回收带析构函数的对象。会滞后,又费时,还要特殊的处理过程。

Class ClassA(){
    ...
    ~ClassA(){
        //释放资源。
    } 
    //以下语法意义与以上同意义,即在C#中析构函数就是C#.NET运行时的终结器
    protected override void Finalize(){ 
        //释放资源。
    }
}

 

所以要它何用?

1、释放只有一个对象可访问的非有限资源。

2、调查垃圾回收的进程。通过在析构函数中修改静态变量,可以看到GC执行了多少次(利用了GC回收带析构函数的对象的特殊机制)。(具体参考《C#Primer Plus中文版第1版》p396)

3、作为释放非托管资源的最终保险措施(下文会提到)

 

回到原先的问题,那该怎么释放对象占有的非内存有限资源呢?

微软鼓励程序员自己使用所谓的"Dispose设计方式",即自己写个Dispose函数去释放资源。而且为了避免意外没有调用Dispose去释放非托管资源,要在析构函数中调用Dispose函数。还有在Dispose函数最后用System.GC.SuppressFinalize()指示GC不要在调用过Dispose之后再调用析构函数(不能两次释放资源)。

在实际使用"Dispose"设计方式时,System提供了IDisposable接口。另外仅有需释放的非托管资源时(托管资源有GC帮忙啦,不需要上保险),才需要重写析构函数去调用Dispose。

具体实现和注意事项请参考以下文章

http://www.cnblogs.com/luminji/archive/2011/03/29/1997812.html

 

有关GC一些重要的事实:

局部变量被设为null以早点被回收是没有意义的。

类的静态变量被创建后不会被回收,除非设为null。而在大系统里,可能有很多静态变量占内存,所以不再用时,要即时设为null。

 

参考

《C#Primer Plus中文版第1版》

http://www.cnblogs.com/luminji/archive/2011/03/29/1997812.html

http://www.cnblogs.com/luminji/archive/2011/04/07/2007205.html

### C# 中的垃圾回收机制及其工作原理 #### 自动内存管理 C# 中的垃圾回收(Garbage Collection, GC)机制是一种自动化的内存管理系统,该系统负责跟踪应用程序中不再使用的对象,并释放这些对象所占用的内存资源[^2]。 #### 托管堆的作用 托管堆用于存储和管理对象。当创建新对象时,CLR会在托管堆上为其分配必要的内存空间。一旦对象变得不可达或者超出作用域,则会被视为垃圾等待被回收[^3]。 #### 回收过程的关键阶段 1. **标记阶段** 垃圾收集器遍历所有根引用(如全局变量、局部静态字段),以及从这些根可以访问到的对象图谱,将可达对象标记为存活状态。未被标记的对象即被认为是可回收的垃圾数据[^5]。 2. **整理阶段** 对于大对象堆(LOH),由于其特殊性不会执行压缩操作;而对于常规的小型对象堆(SOH),则会对已死亡的空间进行紧缩处理以消除碎片化现象,同时调整剩余活动对象的位置以便连续排列。 3. **清除阶段** 清除那些已被确认为无用的数据项,并将其占据的内存返回给操作系统或留给后续的新实例使用。此过程中涉及更新指针和其他内部结构来反映新的布局变化。 #### 强制触发垃圾回收 虽然通常建议让GC自行决定何时启动回收流程,但在某些特定场景下也可以显式调用`GC.Collect()`方法来立即发起一次完整的垃圾搜集动作。不过需要注意的是频繁手动干预可能会带来性能开销甚至负面影响应用的整体表现[^4]。 ```csharp // 不推荐的做法:循环内不断强制垃圾回收 for (int i = 0; i < 90000; i++) { string str = i.ToString(); //模拟一些操作... //这里不应当每次都调用Collect() //GC.Collect(); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值