C#资源释放
转自:http://www.cnblogs.com/psunny/archive/2009/07/07/1518812.html
深刻理解C#中资源释放
今天我的一个朋友看到我写的那篇《C#中用AJAX验证用户登录》时,给我指出了点小毛病。就是在用户登录时,如果用户登录失败,在下面这段代码中,都会new出来一个User对象,如果连续登录失败多次,就会生成多个User对象,而它们在登录失败后已经无用了,依然占据着内存,就算是C#有垃圾回收机制,但不确定什么时候对这些对象进行回收。
然后去网上找了一篇C#资源释放的文章,讲的很透彻,和大家分享一下。
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Model;
using BLL;
public partial class AJAX_CheckUser : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string username = Request.Params["_name"];
string password = Request.Params["_pwd"];
User user = new User();
user.Username = username;
user.Password = password;
string check = BLLCheckUser.Check(user);
Response.Write(check);
}
}
首先,我们需要明确2个概念。
第一个就是很多人用.Net写程序,会谈到托管这个概念。那么.Net所指的资源托管到底是什么意思,是相对于所有资源,还是只限于某一方面资源?很多人对此不是很了解,其实.Net所指的托管只是针对内存这一个方面,并不是对于所有的资源;因此对于Stream,数据库的连接,GDI+的相关对象,还有Com对象等等,这些资源并不是受到.Net管理而统称为非托管资源。而对于内存的释放和回收,系统提供了GC-Garbage
Collector,而至于其他资源则需要手动进行释放。
那么第二个概念就是什么是垃圾,通过我以前的文章,会了解到.Net类型分为两大类,一个就是值类型,另一个就是引用类型。前者是分配在栈上,并不需要GC回收;后者是分配在堆上,因此它的内存释放和回收需要通过GC来完成。GC的全称为“Garbage
Collector”,顾名思义就是垃圾回收器,那么只有被称为垃圾的对象才能被GC回收。也就是说,一个引用类型对象所占用的内存需要被GC回收,需要先成为垃圾。那么.Net如何判定一个引用类型对象是垃圾呢,.Net的判断很简单,只要判定此对象或者其包含的子对象没有任何引用是有效的,那么系统就认为它是垃圾。
明确了这两个基本概念,接下来说说GC的运作方式以及其的功能。内存的释放和回收需要伴随着程序的运行,因此系统为GC安排了独立的线程。那么GC的工作大致是,查询内存中对象是否成为垃圾,然后对垃圾进行释放和回收。那么对于GC对于内存回收采取了一定的优先算法进行轮循回收内存资源。其次,对于内存中的垃圾分为两种,一种是需要调用对象的析构函数,另一种是不需要调用的。GC对于前者的回收需要通过两步完成,第一步是调用对象的析构函数,第二步是回收内存,但是要注意这两步不是在GC一次轮循完成,即需要两次轮循;相对于后者,则只是回收内存而已。
很明显得知,对于某个具体的资源,无法确切知道,对象析构函数什么时候被调用,以及GC什么时候会去释放和回收它所占用的内存。那么对于从C、C++之类语言转换过来的程序员来说,这里需要转变观念。
那么对于程序资源来说,我们应该做些什么,以及如何去做,才能使程序效率最高,同时占用资源能尽快的释放。前面也说了,资源分为两种,托管的内存资源,这是不需要我们操心的,系统已经为我们进行管理了;那么对于非托管的资源,这里再重申一下,就是Stream,数据库的连接,GDI+的相关对象,还有Com对象等等这些资源,需要我们手动去释放。
如何去释放,应该把这些操作放到哪里比较好呢。.Net提供了三种方法,也是最常见的三种,大致如下:
<!--[if
!supportLists]-->1. <!--[endif]-->析构函数;
<!--[if
!supportLists]-->2.
<!--[endif]-->继承IDisposable接口,实现Dispose方法;
<!--[if
!supportLists]-->3.
<!--[endif]-->提供Close方法。
经过前面的介绍,可以知道析构函数只能被GC来调用的,那么无法确定它什么时候被调用,因此用它作为资源的释放并不是很合理,因为资源释放不及时;但是为了防止资源泄漏,毕竟它会被GC调用,因此析构函数可以作为一个补救方法。而Close与Dispose这两种方法的区别在于,调用完了对象的Close方法后,此对象有可能被重新进行使用;而Dispose方法来说,此对象所占有的资源需要被标记为无用了,也就是此对象被销毁了,不能再被使用。例如,常见SqlConnection这个类,当调用完Close方法后,可以通过Open重新打开数据库连接,当彻底不用这个对象了就可以调用Dispose方法来标记此对象无用,等待GC回收。明白了这两种方法的意思后,大家在往自己的类中添加的接口时候,不要歪曲了这两者意思。
接下来说说这三个函数的调用时机,我用几个试验结果来进行说明,可能会使大家的印象更深。
首先是这三种方法的实现,大致如下:
Code
/**////<summary>
/// The class to show three disposal function
///</summary>
public class DisposeClass:IDisposable
{
public void Close()
{
Debug.WriteLine( "Close called!" );
}
~DisposeClass()
{
Debug.WriteLine( "Destructor called!" );
}
IDisposable Members#region IDisposable Members
public void Dispose()
{
// TODO: Add DisposeClass.Dispose implementation
Debug.WriteLin
e( "Dispose called!" );
}
#endregion
}
对于Close来说不属于真正意义上的释放,除了注意它需要显示被调用外,我在此对它不多说了。而对于析构函数而言,不是在对象离开作用域后立刻被执行,只有在关闭进程或者调用GC.Collect方法的时候才被调用,参看如下的代码运行结果。
Code
private void Create()
{
DisposeClass myClass = new DisposeClass();
}
private void CallGC()
{
GC.Collect();
}
// Show destructor
Create();
Debug.WriteLine( "After created!" );
CallGC();
运行的结果为:
After
created!
Destructor called!
显然在出了Create函数外,myClass对象的析构函数没有被立刻调用,而是等显示调用GC.Collect才被调用。
对于Dispose来说,也需要显示的调用,但是对于继承了IDisposable的类型对象可以使用using这个关键字,这样对象的Dispose方法在出了using范围后会被自动调用。例如:
using( DisposeClass myClass = new DisposeClass() )
{
//other operation here
}
如上运行的结果如下:
Dispose
called!
那么对于如上DisposeClass类型的Dispose实现来说,事实上GC还需要调用对象的析构函数,按照前面的GC流程来说,GC对于需要调用析构函数的对象来说,至少经过两个步骤,即首先调用对象的析构函数,其次回收内存。也就是说,按照上面所写的Dispose函数,虽说被执行了,但是GC还是需要执行析构函数,那么一个完整的Dispose函数,应该通过调用GC.SuppressFinalize(this )来告诉GC,让它不用再调用对象的析构函数中。那么改写后的DisposeClass如下:
Code
/**////<summary>
/// The class to show three disposal function
///</summary>
public class DisposeClass:IDisposable
{
public void Close()
{
Debug.WriteLine( "Close called!" );
}
~DisposeClass()
{
Debug.WriteLine( "Destructor called!" );
}
IDisposable Members#region IDisposable Members
public void Dispose()
{
// TODO: Add DisposeClass.Dispose implementation
Debug.WriteLine( "Dispose called!" );
GC.SuppressFinalize( this );
}
#endregion
}
通过如下的代码进行测试。
Code
private void Run()
{
using( DisposeClass myClass = new DisposeClass() )
{
//other operation here
}
}
private void CallGC()
{
GC.Collect();
}
// Show destructor
Run();
Debug.WriteLine( "After Run!" );
CallGC();
运行的结果如下:
Dispose
called!
After Run!
|
析构函数
|
Dispose方法
|
Close方法
|
意义
|
销毁对象
|
销毁对象
|
关闭对象资源
|
调用方式
|
不能被显示调用,会被GC调用
|
需要显示调用
或者通过using语句
|
需要显示调用
|
调用时机
|
不确定
|
确定,在显示调用或者离开using程序块
|
确定,在显示调用时
|
!supportLists]-->1.<!--[endif]-->提供析构函数,避免资源未被释放,主要是指非内存资源;
!supportLists]-->2.<!--[endif]-->对于Dispose和Close方法来说,需要看所定义的类型所使用的资源(参看前面所说),而决定是否去定义这两个函数;
!supportLists]-->3.<!--[endif]-->在实现Dispose方法的时候,一定要加上“GC.SuppressFinalize( this
)”语句,避免再让GC调用对象的析构函数。
C#资源释放的更多相关文章
- 基于webrtc的资源释放问题(二)
基于webrtc的资源释放问题(二) ——建立连接的过程中意外中断 应用背景: 我们在打电话的时候会不会遇到这种情况?打电话的时候未接通之前挂掉了电话,或者在接通之后建立的连接的过程中挂掉电话? 特别 ...
- 基于webrtc的资源释放问题(一)
基于webrtc的资源释放问题(一) ——重复释放webrtc的相关资源 背景: 视频通讯大都只是作为一个功能存在于各种应用中,比如微信,qq .既然只是应用的一部分,这样就涉及反复的开启和关闭视频通 ...
- TList,TObjectList 使用——资源释放
TOjectList = Class (Tlist); TOjectList继承Tlist,从名字上看就可以知道它是专门为对象列表制作的,那么他到底丰富了那些功能呢? 首先是 TObject 作为对象 ...
- Delphi中关于资源释放(Free,Relealse,FreeAndNil)
根据日常编程经验,得出一些Delphi中关于资源释放的体会. 假如有对象Obj为TObject类型: 1) Obj.Free直接释放资源后,调用OnDestroy事件,但是没有将Obj指针值置为Nil ...
- 深刻理解C#中资源释放
今天我的一个朋友看到我写的那篇<C#中用AJAX验证用户登录>时,给我指出了点小毛病.就是在用户登录时,如果用户登录失败,在下面这段代码中,都会new出来一个User对象,如果连续登录失败 ...
- Unity3d: 资源释放时存储空间不足引发的思考和遇到的问题
手机游戏第一次启动基本上都会做资源释放的操作,这个时候需要考虑存储空间是否足够,但是Unity没有自带获取设备存储空间大小的 接口,需要调用本地方法分别去android或ios获取,这样挺麻烦的.而且 ...
- .net 资源释放(托管资源和非托管资源)
1.托管资源 像int.float.DateTime等都是托管资源:net中80%的资源都是托管资源: 托管资源的回收通过GC(垃圾回收器)自动释放分配给该对象的内存,但无法预测进行垃圾回收的时间,我 ...
- C#资源释放及Dispose、Close和析构方法
https://www.cnblogs.com/luminji/archive/2011/01/05/1926468.html C#资源释放及Dispose.Close和析构方法 备注:此文的部分 ...
- atitit.资源释放机制--attilax总结
atitit.资源释放机制--attilax总结 1. .全手工, 1 2. 引用计数, 1 2.1. 成本也显而易见. 1 2.2. 循环引用的问题, 2 2.3. 引用计数方式事实上也有经典的卡顿 ...
随机推荐
- Stack and Heap 堆和栈的区别
在和计算机内存打交道时,我们一定会碰到堆和栈,这两个东西很容易搞混,那么现在就来梳理一下二者的关系. 栈是用来静态分配内存的而堆是动态分配内存的,它们都是存在于计算机内存之中. 栈的分配是在程序编译的 ...
- v.size() return size_t not int 返回无符号整型数
In the C++ STL, the vector size() function return size_t, which is unsigned int, not int. So imagine ...
- 三、saltstack证书管理
192.168.1.65 super65.cn master192.168.1.66 super66.cn minion saltstack使用SSL签证的方式进行安全认证. minion上线 ...
- mysql insert插入新形式,再也不需要拼接多重insert啦
注意一下,不能省略表中的任何字段.包括自增id.而且字段的顺序必须和插入表一致 原理是“表插表” INSERT INTO prod_attr select A.* from ( SELECT AS p ...
- GC-垃圾回收
代:0代,1代,2代: 所谓第几代,指经历过GC回收的次数. 回收算法: 1.确认需要检查的代. 在分配新对象时, 如果第0代已满,则进行检查:如果第1代已满,则进行检查:第2代同理: 如第0代没有足 ...
- FZU 2124 bfs+vis记录
第一次团队训练赛的题 自己看完题没看到不能用舌头吃道具..以为是什么贪心混合bfs..果断放弃..悄悄的背锅了 然后其实比较简单 只是利用vis记录的时候要分两种状态记录 有没有道具 每到一个地方 就 ...
- 《Java核心技术卷二》笔记(三)正则表达式
正则表达式语法 一个正则表达式描述了字符串的构成规则(模式).如果一个具体的字符串正好符合正则表达式描述的这个规则,这个字符串就是与表达式匹配的.先看一下怎么描述这种规则,也就是正则表达式语法.正则表 ...
- NV OIT algorithm : Depth peeling is a fragment-level depth sorting technique
https://developer.nvidia.com/content/interactive-order-independent-transparency Correctly rendering ...
- Redis 笔记与总结3 list 类型
redis 版本 [root@localhost ~]# redis-server --version Redis server v= sha=: malloc=jemalloc- bits= bui ...
- 如何开发ecshop支付插件
如何开发ecshop支付插件 ecshop模板网 / 2014-06-03 目标一:搞懂ecshop的支付流程 选完商品,进入购物车页面,点击“结算中心”,页面跳转到flow.php?step=che ...