C#或者.NET下的强制垃圾回收办法

转载 2011年03月16日 17:21:00

  • 8185

今天来谈谈C#的GC,也就是垃圾回收机制,非常的受教,总结如下
首先:谈谈托管,什么叫托管,我的理解就是托付C#运行环境帮我们去管理,在这个运行环境中可以帮助我们开辟内存和释放内存,开辟内存一般用new,内存是随机分配的,释放主要靠的是GC也就是垃圾回收机制。哪么有两个大问题 1.GC可以回收任何对象吗?2.GC什么时候来回收对象?回收那些对象?
对于第一个问题,GC可以回收任何对象吗?我是这样理解的,首先要明白一点,C#在强大也管不到非托管代码?哪么什么是非托管代码呢?比如stream(文件),connection(数据库连接),COM(组件)等等。。哪么这些对象是需要进行连接的,比如说我们写这样一句话FileStream fs = new FileStream(“d://a.txt”,FileMode.Open);实际上已经创建了和d://a.txt的连接,如果重复两次就会报错。哪么fs这个对象叫做非托管对象,也就是说C#

不能自动去释放和d://a.txt的连接。哪么对于非托管的代码怎么办,一会我来说。
    对于第二个问题,GC什么时候来回收,回收什么对象?我想后面的就不用我说了,当然是回收托管对象了。但是GC什么时候回收?是这样的:GC是随机的,没有人知道他什么时候来,哪么我写了一个例子,证明这一点
private void button1_Click(object sender, EventArgs e)

{

AA a = new AA();

AA b = new AA();

AA c = new AA();

AA d = new AA();

}

public class AA{}

在讲这个例子之前,要明白什么被称之为垃圾,垃圾就是一个内存区域,没有被任何引用指向,或者不再会被用到。哪么在第一次点击按钮的时候会生成4个对象,第二次点击按钮的时候也会生成4个对象,但是第一次生成的4个对象就已经是垃圾了,因为,第一次生成的4个对象随着button1_Click函数的结束而不会再被调用(或者说不能再被调用),哪么这个时候GC就会来回收吗?不是的!我说了GC是随机的,哪么你只管点你的,不一会GC就会来回收的(这里我们可以认为,内存中存在一定数量的垃圾之后,GC会来),要证明GC来过我们把AA类改成
public class AA

{

~AA()

{

MessageBox.Show("析构函数被执行了");

}

}

要明白,GC清理垃圾,实际上是调用析构函数,但是这些代码是托管代码(因为里面没有涉及到Steam,Connection等。。)所以在析构函数中,我们可以只写一个MsgBox来证明刚的想法;这个时候,运行你的程序,一直点击按钮,不一会就会出现一大堆的“析构函数被执行了”…

好了,然后让我们看看能不能改变GC这种为所欲为的天性,答案是可以的,我们可以通过调用GC.Collect();来强制GC进行垃圾回收,哪么button1_Click修改如下
private void button1_Click(object sender, EventArgs e)

{

AA a = new AA();

AA b = new AA();

AA c = new AA();

AA d = new AA();

GC.Collect();

}

哪么在点击第一次按钮的时候,生成四个对象,然后强制垃圾回收,这个时候,会回收吗?当然不会,因为,这四个对象还在执行中(方法还没结束),当点第二次按钮的时候,会出现四次"析构函数被执行了",这是在释放第一次点击按钮的四个对象,然后以后每次点击都会出现四次"析构函数被执行了",哪么最后一次的对象什么时候释放的,在关闭程序的时候释放(因为关闭程序要释放所有的内存)。

好了,现在来谈谈非托管代码,刚才说过,非托管代码不能由垃圾回收释放,我们把AA类改成如下
public class AA

{

FileStream fs = new FileStream("D://a.txt",FileMode.Open);

~AA()

{

MessageBox.Show("析构函数被执行了");

}

}

private void button1_Click(object sender, EventArgs e)

{

AA a = new AA();

}

如果是这样一种情况,哪么第二次点击的时候就会报错,原因是一个文件只能创建一个连接。哪么一定要释放掉第一个资源,才可以进行第二次的连接。哪么首先我们想到用GC.Collect(),来强制释放闲置的资源,修改代码如下:
private void button1_Click(object sender, EventArgs e)

{

GC.Collect();

AA a = new AA();

}

哪么可以看到,第二次点按钮的时候,确实出现了“析构函数被执行了“,但是程序仍然错了,原因前面我说过,因为Stream不是托管代码,所以C#不能帮我们回收,哪怎么办?
自己写一个Dispose方法;去释放我们的内存。代码如下:
public class AA:IDisposable

{

FileStream fs = new FileStream("D://a.txt",FileMode.Open);

~AA()

{

MessageBox.Show("析构函数被执行了");

}

#region IDisposable 成员

public void Dispose()

{

fs.Dispose();

MessageBox.Show("dispose执行了");

}

#endregion

}

好了,我们看到了,继承IDisposable接口以后会有一个Dispose方法(当然了,你不想继承也可以,但是接口给我们提供一种规则,你不愿意遵守这个规则,就永远无法融入整个团队,你的代码只有你一个人能看懂),好了闲话不说,这样一来我们的button1_Click改为private void button1_Click(object sender, EventArgs e)

{

AA a = new AA();

a.Dispose();

}

我们每次点击之后,都会发现执行了“dispose执行了”,在关闭程序的时候仍然执行了“析构函数被执行了”这意味了,GC还是工作了,哪么如果程序改为:
private void button1_Click(object sender, EventArgs e)

{

AA a = new AA();

a.Dispose();

GC.Collect();

}

每次都既有“dispose执行了又有”“析构函数被执行了”,这意味着GC又来捣乱了,哪么像这样包含Stream connection的对象,就不用GC来清理了,只需要我们加上最后一句话GC.SuppressFinalize(this)来告诉GC,让它不用再调用对象的析构函数中。那么改写后的AA的dispose方法如下:

public void Dispose()

{

fs.Dispose();

MessageBox.Show("dispose执行了");

GC.SuppressFinalize(this);
        }

本文下载自CSDN下载频道。

-------------------------------------------------------------------------------------------

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

那.Net程序员在编程时应该怎么做,有没有一种既简单又有有效的方法来处理内在回收。愚人作以下建议,望各路高手不吝赐教:

1,对于不包涵或没有引用(直接或间接)非托管资源的类,特别是作用如同Struct的实体类,析构、终结器、Dispose均不采用。

2,对于包涵非托管资源的类,如数据库连接对象,文件句柄等,应继承IDispose接口,在Dispose方法中清理非托管对象。客户代码用using(…){}格式显示调用Dispose。如果继承了IDispose接口,Dispose方法就不要留空,这样没有任何意义。除了构造器,任何方法体留空都有害无益。

3,所有自定义类一般均不建议显式声明析构函数、Finalize方法。

时间: 2024-10-17 18:18:41

C#或者.NET下的强制垃圾回收办法的相关文章

java 之 对象与垃圾回收

1.垃圾回收机制只负责回收堆内存中的对象,不会回收任何物理资源(如数据库连接,网络IO等资源) 2.程序无法精确控制垃圾回收的运行,垃圾回收会在合适的时候运行.当对象永久的失去引用后,系统会在合适的时间回收其占用的内存. 3.在垃圾回收机制回收任何对象之前,总会先调用它的finalize()方法. 对象在内存中的状态: 可 达 状 态:当一个对象被创建后,若有一个以上的引用变量引用他,则这个对象在程序中处于可达状态,程序可通过引用变量来调用该对象的实例变量和方法. 可恢复状态:如果程序中某个变量

Java垃圾回收(一)

Java垃圾回收(一) 在Java中,它的内存管理包括两个方面:内存分配和内存回收,这两个方面的工作都是由JVM自动完成的,降低了Java程序员的学习难度,避免了像C/C++直接操作内存的危险.但这也使很多程序员不关心内存分配的问题,导致很多程序低效耗费内存. Java语言规范没有明确的说明JVM使用哪种垃圾回收算法.一般常用的算法有下列几种: 引用记数法(Reference Counting Collector) tracing算法(Tracing Collector) compacting算

c# -- 对象销毁和垃圾回收

有些对象需要显示地销毁代码来释放资源,比如打开的文件资源,锁,操作系统句柄和非托管对象.在.NET中,这就是所谓的对象销毁,它通过IDisposal接口来实现.不再使用的对象所占用的内存管理,必须在某个时候回收:这个被称为无用单元收集的功能由CLR执行. 对象销毁和垃圾回收的区别在于:对象销毁通常是明确的策动:而垃圾回收完全是自动地.换句话说,程序员负责释放文件句柄,锁,以及操作系统资源:而CLR负责释放内存. 本章将讨论对象销毁和垃圾回收,还描述了C#处理销毁的一个备选方案--Finalize

垃圾回收与对象的引用

垃圾回收 当程序创建对象.数组等引用类型实体时,系统就会在对内存中为之分配一块内存区,对象就保存在这块内存区中,当这块内存不再被任何引用变量引用时,这块内存就变成垃圾,等待垃圾回收机制进行回收. 垃圾回收机制具有如下特点: 垃圾回收机制只负责回收堆内存中的对象,不会回收任何物理资源. 程序无法精确控制垃圾回收的运行,垃圾回收会在合适的时候进行.当对象永久性地失去引用后,系统就会在合适的时候回收它所占的内存. 在垃圾回收机制回收任何对象之前,总会调用它的finalize()方法,可以重写该方法,让

Unity优化方向——优化Unity游戏中的垃圾回收

介绍 当我们的游戏运行时,它使用内存来存储数据.当不再需要该数据时,存储该数据的内存将被释放,以便可以重用.垃圾是用来存储数据但不再使用的内存的术语.垃圾回收是该内存再次可用以进行重用的进程的名称. Unity使用垃圾回收作为管理内存的一部分.如果垃圾回收发生得太频繁或者有太多工作要做,我们的游戏可能会表现不佳,这意味着垃圾回收是导致性能问题的常见原因. 在本文中,我们将了解垃圾回收如何工作的,什么时候发生垃圾回收,以及如何有效地使用内存,从而最小化垃圾回收对游戏的影响. 诊断垃圾回收的问题 垃

重温CLR(十五) 托管堆和垃圾回收

本章要讨论托管应用程序如何构造新对象,托管堆如何控制这些对象的生存期,以及如何回收这些对象的内存.简单地说,本章要解释clr中的垃圾回收期是如何工作的,还要解释相关的性能问题.另外,本章讨论了如何设计应用程序来最有效地使用内存. 托管堆基础 每个程序都要使用这样或那样的资源,包括文件.内存缓冲区.屏幕空间.网络连接.数据库资源等.事实上,在面向对象的环境中,每个类型都代表可提供程序使用的一种资源.要使用这些资源,必须为代表资源的类型分配内存.以下是访问一个资源所需的步骤 1 调用IL指令newo

如何使用 Chrome 和 DevTools 查找影响页面性能的内存问题,包括内存泄漏、内存膨胀和频繁的垃圾回收

了解如何使用 Chrome 和 DevTools 查找影响页面性能的内存问题,包括内存泄漏.内存膨胀和频繁的垃圾回收. TL;DR 使用 Chrome 的任务管理器了解您的页面当前正在使用的内存量. 使用 Timeline 记录可视化一段时间内的内存使用. 使用堆快照确定已分离的 DOM 树(内存泄漏的常见原因). 使用分配时间线记录了解新内存在 JS 堆中的分配时间. 概览 在 RAIL 性能模型的精髓中,您的性能工作的焦点应是用户. 内存问题至关重要,因为这些问题经常会被用户察觉. 用户可通

深入理解JAVA虚拟机之JVM性能篇---垃圾回收

一.基本垃圾回收算法 1. 按基本回收策略分 1) 引用计数(Reference Counting)  对象增加一个引用,即增加一个计数,删除一个引用则减少一个计数.垃圾回收时,只用收集计数为0的对象.此算法最致命的是无法处理循环引用的问题. 2)标记-清除(Mark-Sweep)  执行分两阶段.第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除. 缺点是此算法需要暂停整个应用,同时会产生内存碎片. 3)复制(Copying) 把内存空间划为两个相等的区域,每

python的垃圾回收机制

进程空间 进程运行时需要在内核中占据一段内存空间,用以存储程序和数据. 每个进程空间分布如下所示: 进程空间的结构 text段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域.在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等. data段:数据段(data segment)通常用来存放程序中已初始化的全局变量数据段属于静态内存分配. bss段:bss(Block Started by Symbol) 通常用来存放程序中未初始化的