编写高质量代码改善C#程序的157个建议——建议52:及时释放资源
建议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:及时释放资源的更多相关文章
- 编写高质量代码改善C#程序的157个建议[1-3]
原文:编写高质量代码改善C#程序的157个建议[1-3] 前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理 ...
- 读书--编写高质量代码 改善C#程序的157个建议
最近读了陆敏技写的一本书<<编写高质量代码 改善C#程序的157个建议>>书写的很好.我还看了他的博客http://www.cnblogs.com/luminji . 前面部 ...
- 编写高质量代码改善C#程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试
建议157:从写第一个界面开始,就进行自动化测试 如果说单元测试是白盒测试,那么自动化测试就是黑盒测试.黑盒测试要求捕捉界面上的控件句柄,并对其进行编码,以达到模拟人工操作的目的.具体的自动化测试请学 ...
- 编写高质量代码改善C#程序的157个建议——建议156:利用特性为应用程序提供多个版本
建议156:利用特性为应用程序提供多个版本 基于如下理由,需要为应用程序提供多个版本: 应用程序有体验版和完整功能版. 应用程序在迭代过程中需要屏蔽一些不成熟的功能. 假设我们的应用程序共有两类功能: ...
- 编写高质量代码改善C#程序的157个建议——建议155:随生产代码一起提交单元测试代码
建议155:随生产代码一起提交单元测试代码 首先提出一个问题:我们害怕修改代码吗?是否曾经无数次面对乱糟糟的代码,下决心进行重构,然后在一个月后的某个周一,却收到来自测试版的报告:新的版本,没有之前的 ...
- 编写高质量代码改善C#程序的157个建议——建议154:不要过度设计,在敏捷中体会重构的乐趣
建议154:不要过度设计,在敏捷中体会重构的乐趣 有时候,我们不得不随时更改软件的设计: 如果项目是针对某个大型机构的,不同级别的软件使用者,会提出不同的需求,或者随着关键岗位人员的更替,需求也会随个 ...
- 编写高质量代码改善C#程序的157个建议——建议153:若抛出异常,则必须要注释
建议153:若抛出异常,则必须要注释 有一种必须加注释的场景,即使异常.如果API抛出异常,则必须给出注释.调用者必须通过注释才能知道如何处理那些专有的异常.通常,即便良好的命名也不可能告诉我们方法会 ...
- 编写高质量代码改善C#程序的157个建议——建议152:最少,甚至是不要注释
建议152:最少,甚至是不要注释 以往,我们在代码中不写上几行注释,就会被认为是钟不负责任的态度.现在,这种观点正在改变.试想,如果我们所有的命名全部采用有意义的单词或词组,注释还有多少存在的价值. ...
- 编写高质量代码改善C#程序的157个建议——建议151:使用事件访问器替换公开的事件成员变量
建议151:使用事件访问器替换公开的事件成员变量 事件访问器包含两部分内容:添加访问器和删除访问器.如果涉及公开的事件字段,应该始终使用事件访问器.代码如下所示: class SampleClass ...
- 编写高质量代码改善C#程序的157个建议——建议150:使用匿名方法、Lambda表达式代替方法
建议150:使用匿名方法.Lambda表达式代替方法 方法体如果过小(如小于3行),专门为此定义一个方法就会显得过于繁琐.比如: static void SampeMethod() { List< ...
随机推荐
- 关于android开发环境中sdk和adt更新到22.6之后多了appcompat_v7
昨天我打开Eclipse更新了一下sdk和adt到22.6,更新一切都很顺利,很开心的样子,可以新建一个工程时发现多了一个appcompat_v7这个东西,一下子就把小编怔住了,后来才发现这是官方的一 ...
- FPGA前世今生(四)
前几期我们一直介绍FPGA内部资源,今天我们将用最后的篇幅来介绍剩下的内部资源部分,结束我们FPGA的前世今生.之所以起名字为FPGA前世今生,其实就是介绍一下FPGA内部资源,前世的内部结构资源就不 ...
- git常用命令小结
1.ssh连接方式 公钥生成ssh-keygen -t rsa -C "764432054@qq.com"在用户家目录下的.ssh目录下生成 id_rsa ,id_rsa.pub ...
- MongoDB Data Model 浅谈
MongoDB 对于数据的 schema 要求很灵活. 与 MySQL 相比,collection 并不会强制文档的结构.(MySQL 在定义表时, 需要指定有哪些字段.类型.展示长度等) 因此,插入 ...
- 1127 ZigZagging on a Tree
题意:中序序列+后序序列构建二叉树,之字形输出其层序序列. 思路:在结点的数据域中额外增加一个layer表示结点所在的层次,并定义vector<int> zigzag[maxn]存放最终结 ...
- 使用QuartZ.net来做定时计划任务 ; 值不能为 null。 参数名: assemblyString
1. 当异常的时候, 发现需要的类名, 没有取到, 然后就发生异常了 2. 分析: 业务层调用数据层, 数据层去掉配置的时候, 发现配置文件中根本就没有配置, 这个时候使用反射来取, 肯定是拿不到的, ...
- 第13章 Spring框架的设计理念与设计模式分析
13.1 Spring的骨骼架构 最核心的组件是:Core, Context, Bean. 13.1.1 Spring的设计理念 Spring最核心的组件是Bean组件.Spring解决的最关键的问题 ...
- linux中内存使用,swap,cache,buffer的含义总结
首先介绍一下linux中内存是如何使用的.当有应用需要读写磁盘数据时,由系统把相关数据从磁盘读取到内存,如果物理内存不够,则把内存中的部分数据导入到磁盘,从而把磁盘的部分空间当作虚拟内存来使用,也称为 ...
- C语言运算符优先级和口诀 (转)
一共有十五个优先级: 1 () [] . -> 2 ! ~ -(负号) ++ -- &(取变量地址)* (type)(强制类型) sizeof 3 ...
- OSCache安装
OSCache是一个基于web应用的组件,他的安装工作主要是对web应用进行配置,大概的步骤如下: 1. 下载.解压缩OSCachehttps://java.net/downloads/oscache ...