建议52:及时释放资源

垃圾回收机制自动为我们隐式地回收了资源(垃圾回收器会自动调用终结器),那我们为什么要主动释放资源呢?

private void buttonOpen_Click(object sender,EventArgs e)
{
  FileStream fileStream = new FileStream(@"c:\test.txt",FileMode.Open);
} private void buttonGC_Click(object sender,EventArgs e)
{
  System.GC.Collect();
}

这是一个WinForm窗体程序的例子,在这个示例中,单击一个按钮负责打开一个文件,单击另一个按钮负责回收说有“代”(代的概念会在下文详细指出)的垃圾。如果连续两次单击打开文件的按钮,系统就会报错:

IOException:文件“c:\test.txt”正由另外一进程使用,因此该进程无法访问此文件。

如果先单击打开文件的按钮,继而再单击清理按钮,则运行正常。

现在来分析:在打开文件的方法中,方法执行完毕后,由于局部变量fileStream在程序中已经没有任何地方引用了,所以它会在下一次垃圾回收时被运行时标记为垃圾。那么,什么时候会进行下一次垃圾回收呢,后者说垃圾回收器什么时候才开始真正进行回收工作呢?微软官方的解释是,当满足以下条件之一时将发生垃圾回收:

  • 系统具有低的物理内存。
  • 由托管堆上已分配的对象使用的内存超出了可接受的阈值。 这意味着可接受的内存使用的阈值已超过托管堆。 随着进程的运行,此阈值会不断地进行调整。
  • 调用 GC.Collect 方法。 几乎在所有情况下,您都不必调用此方法,因为垃圾回收器会持续运行。 此方法主要用于特殊情况和测试。

不及时释放资源是对系统的一种极大的浪费,这种浪费还会干扰程序的正常运行(如在本实例中,由于它始终占着文件资源,导致我们不能再次使用这个文件资源了)。

若果类型本身继承了IDisposable接口,垃圾回收机制虽然会自动帮我们释放资源,但是这个过程却延长了,因为它不是在一次回收中完成所有的清理工作。本例中因为fileStream继承了IDisposable接口,故第一次进行垃圾回收时,垃圾回收器会调用fileStream的终结器,然后等待下一次的垃圾回收,这时fileStream对象才有可能被真正的回收掉。

我们来改进这个程序:

private void buttonOpen_Click(object sender,EventArgs e)
{
FileStream fileStream = new FileStream(@"c:\test.txt",FileMode.Open);
fileStream.Dispose();
}

但是如果第一行代码出现异常,就永远执行不了filsStream.Dispose()了。再次改进:

private void buttonOpen_Click(object sender,EventArgs e)
{
try
{
FileStream fileStream = new FileStream(@"c:\test.txt",FileMode.Open);
}
finally
{
fileStream.Dispose();
}
}

使用语法糖“using"关键字进一步简化:

private void buttonOpen_Click(object sender,EventArgs e)
{
using(FileStream fileStream = new FileStream(@"c:\test.txt",FileMode.Open))
{ }
}

关于“代数”:一共分为3代:第0代、第1代、第2代。

  • 第 0 代:这是最年轻的代,其中包含短生存期对象。 短生存期对象的一个示例是临时变量。 垃圾回收最常发生在此代中。新分配的对象构成新一代的对象并且为隐式的第 0 代回收,除非它们是大对象,在这种情况下,它们将进入第 2 代回收中的大对象堆。大多数对象通过第 0 代中的垃圾回收进行回收,不会保留到下一代。
  • 第 1 代:这一代包含短生存期对象并用作短生存期对象和长生存期对象之间的缓冲区。
  • 第 2 代:这一代包含长生存期对象。 长生存期对象的一个示例是服务器应用程序中的一个包含在进程期间处于活动状态的静态数据的对象。

当条件得到满足时,垃圾回收将在特定代上发生。 回收某个代意味着回收此代中的对象及其所有更年轻的代。 第 2 代垃圾回收也称为完整垃圾回收,因为它回收所有代上的所有对象(即,托管堆中的所有对象)。

幸存和提升

垃圾回收中未回收的对象也称为幸存者,并会被提升到下一代。 在第 0 代垃圾回收中幸存的对象将被提升到第 1 代;在第 1 代垃圾回收中幸存的对象将被提升到第 2 代;而在第 2 代垃圾回收中幸存的对象将仍为第 2 代。

当垃圾回收器检测到某个代中的幸存率很高时,它会增加该代的分配阈值,因此下一次回收将会获取一个非常大的回收内存。 CLR 会在以下两个优先级别之前进行平衡:不允许应用程序的工作集获取太大内存以及不允许垃圾回收花费太多时间。

暂时代和暂时段

因为第 0 代和第 1 代中的对象的生存期较短,因此,这些代被称为暂时代。

暂时代必须在称为暂时段的内存段中进行分配。 垃圾回收器获取的每个新段将成为新的暂时段,并包含在第 0 代垃圾回收中幸存的对象。 旧的暂时段将成为新的第 2 代段。

更多内容可参考MSDN:垃圾回收的基础

转自:《编写高质量代码改善C#程序的157个建议》陆敏技

编写高质量代码改善C#程序的157个建议——建议52:及时释放资源的更多相关文章

  1. 编写高质量代码改善C#程序的157个建议[1-3]

    原文:编写高质量代码改善C#程序的157个建议[1-3] 前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理 ...

  2. 读书--编写高质量代码 改善C#程序的157个建议

    最近读了陆敏技写的一本书<<编写高质量代码  改善C#程序的157个建议>>书写的很好.我还看了他的博客http://www.cnblogs.com/luminji . 前面部 ...

  3. 编写高质量代码改善C#程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试

    建议157:从写第一个界面开始,就进行自动化测试 如果说单元测试是白盒测试,那么自动化测试就是黑盒测试.黑盒测试要求捕捉界面上的控件句柄,并对其进行编码,以达到模拟人工操作的目的.具体的自动化测试请学 ...

  4. 编写高质量代码改善C#程序的157个建议——建议156:利用特性为应用程序提供多个版本

    建议156:利用特性为应用程序提供多个版本 基于如下理由,需要为应用程序提供多个版本: 应用程序有体验版和完整功能版. 应用程序在迭代过程中需要屏蔽一些不成熟的功能. 假设我们的应用程序共有两类功能: ...

  5. 编写高质量代码改善C#程序的157个建议——建议155:随生产代码一起提交单元测试代码

    建议155:随生产代码一起提交单元测试代码 首先提出一个问题:我们害怕修改代码吗?是否曾经无数次面对乱糟糟的代码,下决心进行重构,然后在一个月后的某个周一,却收到来自测试版的报告:新的版本,没有之前的 ...

  6. 编写高质量代码改善C#程序的157个建议——建议154:不要过度设计,在敏捷中体会重构的乐趣

    建议154:不要过度设计,在敏捷中体会重构的乐趣 有时候,我们不得不随时更改软件的设计: 如果项目是针对某个大型机构的,不同级别的软件使用者,会提出不同的需求,或者随着关键岗位人员的更替,需求也会随个 ...

  7. 编写高质量代码改善C#程序的157个建议——建议153:若抛出异常,则必须要注释

    建议153:若抛出异常,则必须要注释 有一种必须加注释的场景,即使异常.如果API抛出异常,则必须给出注释.调用者必须通过注释才能知道如何处理那些专有的异常.通常,即便良好的命名也不可能告诉我们方法会 ...

  8. 编写高质量代码改善C#程序的157个建议——建议152:最少,甚至是不要注释

    建议152:最少,甚至是不要注释 以往,我们在代码中不写上几行注释,就会被认为是钟不负责任的态度.现在,这种观点正在改变.试想,如果我们所有的命名全部采用有意义的单词或词组,注释还有多少存在的价值. ...

  9. 编写高质量代码改善C#程序的157个建议——建议151:使用事件访问器替换公开的事件成员变量

    建议151:使用事件访问器替换公开的事件成员变量 事件访问器包含两部分内容:添加访问器和删除访问器.如果涉及公开的事件字段,应该始终使用事件访问器.代码如下所示: class SampleClass ...

  10. 编写高质量代码改善C#程序的157个建议——建议150:使用匿名方法、Lambda表达式代替方法

    建议150:使用匿名方法.Lambda表达式代替方法 方法体如果过小(如小于3行),专门为此定义一个方法就会显得过于繁琐.比如: static void SampeMethod() { List< ...

随机推荐

  1. 【转】jmeter压力测试

    jmeter压力测试 Apache JMeter是Apache组织开发的基于Java的压力测试工具.用于对软件做压力测试,它最初被设计用于Web应用测试但后来扩展到其他测试领域, 是压力测试的首选软件 ...

  2. mysql实战优化之八:关联查询优化

    1. 多表连接类型 1. 笛卡尔积(交叉连接) 在MySQL中可以为CROSS JOIN或者省略CROSS即JOIN,或者使用','  如: 由于其返回的结果为被连接的两个数据表的乘积,因此当有WHE ...

  3. loadrunner 学习 1 —— 关于loadrunner的安装/破解

    从网上下载了loadrunner 11, .iso格式的镜像文件,百度一下,发现要用专门的软件才能在windows7 下安装 iso,我选的是 软件魔方. 安装完破解时,略有曲折, 主要是要以管理员的 ...

  4. UE4子弹特效

    转自:http://blog.ch-wind.com/ue4-projectile-visual-effects/ 子弹使用抛体就可以实现了,但是要让其看起来更加真实,则可能需要加上一些粒子特效. 当 ...

  5. Linux学习笔记 - Shell 运算符篇

    Shell 运算符分类 Shell 和其他编程语言一样,支持多种运算符,包括: 算数运算符 关系运算符 布尔运算符 字符串运算符 文件测试运算符 算数运算符 首先,使用 shell 算数运算符是,需要 ...

  6. Unity3D的坑系列:你真想发布WinPhone版吗?

    Unity 4.2加入了支持WinPhone发布,本来是一件令人开心的事情,不过最近听了Unity技术支持的一个事情后就发现,原来发布WinPhone版也是一个坑. 实际上如果你用Unity做小游戏发 ...

  7. maven项目依赖包问题

    问题 maven传递依赖 解决方案   前段时间,开发中遇到一个关于maven依赖包的问题:由于业务需要,支付网关对账代码中的slf4j-api包需要更新,原包为1.5.8版本,需要更新到1.6.4版 ...

  8. Android 4学习(9):用户界面 - THE ANDROID WIDGET TOOLBOX

    Android内置了很多View,包括: TextView EditText Chronometer ListView Spinner Button ToggleButton ImageButton ...

  9. Mycat实战之主键数据库自增方式

    创建一个 person表,主键为Id,hash方式分片,主键自增(采用数据库方式) #person表结构如下 Id,主键,Mycat自增主键 name,字符串,16字节最长 school,毕业学校,数 ...

  10. 【原】Coursera—Andrew Ng机器学习—编程作业 Programming Exercise 3—多分类逻辑回归和神经网络

    作业说明 Exercise 3,Week 4,使用Octave实现图片中手写数字 0-9 的识别,采用两种方式(1)多分类逻辑回归(2)多分类神经网络.对比结果. (1)多分类逻辑回归:实现 lrCo ...