一起谈.NET技术,引用类型赋值为null与加速垃圾回收

  在标准的Dispose模式中,提到了需要及时释放资源,却并没有进一步细说让引用等于null是否有必要。

  有一些人认为等于null可以帮助垃圾回收机制早点发现并标识对象是垃圾。其他人则认为这没有任何帮助。是否赋值为null的问题首先在方法的内部被人提起。现在,为了更好的阐述提出的问题,我们来撰写一个Winform窗体应用程序。如下:


private void button1_Click(object sender, EventArgs e)
{
Method1();
Method2();
}

private void button2_Click(object sender, EventArgs e)
{
GC.Collect();
}

private void Method1()
{
SimpleClass s = new SimpleClass("method1");
s = null;
//其它无关工作代码(这条注释源于回应回复的朋友的质疑)
}
private void Method2()
{
SimpleClass s = new SimpleClass("method2");
}
}

class SimpleClass
{
string m_text;

public SimpleClass(string text)
{
m_text = text;
}

~SimpleClass()
{
MessageBox.Show(string.Format("SimpleClass Disposed, tag:{0}", m_text));
}
}

  先点击按钮1,再点击按钮2释放,我们会发现:

  q 方法Method2中的对象先被释放,虽然它在Method1之后被调用;

  q 方法Method2中的对象先被释放,虽然它不像Method1那样为对象引用赋值为null;

  在CLR托管应用程序中,存在一个根的概念,类型的静态字段、方法参数以及局部变量都可以作为根存在(值类型不能作为根,只有引用类型的指针才能作为根)。

  上面的两个方法中各自的局部变量,在代码运行过程中会在内存中各自创建一个根.在一次垃圾回收中,垃圾回收器会沿着线程栈上行检查根。检查到方法内的根时,如果发现没有任何一个地方引用了局部变量,则不管是否为变量赋值为null,都意味着该根已经被停止掉。然后垃圾回收器发现该根的引用为空,同时标记该根可被释放,这也表示着Simple类型对象所占用的内存空间可被释放。所以,在上面的这个例子中,为s指定为null丝毫没有意义(方法的参数变量也是这种情况)。

  更进一步的事实是,JIT编译器是一个经过优化的编译器,无论我们是否在方法内部为局部变量赋值为null,该语句都会被忽略掉

s = null;

  在我们将项目设置为Release模式下,上面的这行代码将根本不会被编译进运行时内。

  正式由于上面这样的分析,很多人认为为对象赋值为null完全没有必要。但是,在另外一种情况下,却要注意及时为变量赋值为null。那就是类型的静态字段。为类型对象赋值为null,并不意味着同时为类型的静态字段赋值为null:


private void button1_Click(object sender, EventArgs e)
{
SimpleClass s = new SimpleClass("test");
}

private void button2_Click(object sender, EventArgs e)
{
GC.Collect();
}
}

class SimpleClass
{
static AnotherSimpleClass asc = new AnotherSimpleClass();
string m_text;

public SimpleClass(string text)
{
m_text = text;
}

~SimpleClass()
{
//asc = null;
MessageBox.Show(string.Format("SimpleClass Disposed, tag:{0}", m_text));
}
}

class AnotherSimpleClass
{
~AnotherSimpleClass()
{
MessageBox.Show("AnotherSimpleClass Disposed");
}
}

  以上代码运行的结果使我们发现,当执行垃圾回收,当类型SampleClass对象被回收的时候,类型的静态字段asc并没有被回收。

  必须要将SimpleClass的终结器中注释的那条代码启用。

  字段asc才能被正确释放(注意,要点击两次释放按钮。这是因为一次垃圾回收会仅仅首先执行终结器)。之所以静态字段不被释放(同时赋值为null语句也不会像局部变量那样被运行时编译器优化掉),是因为类型的静态字段一旦被创建,该根就一直存在。所以垃圾回收器始终不会认为它是一个垃圾。非静态字段不存在这个问题。将asc改为非静态,再次运行上面的代码,会发现asc随着类型的释放而被释放。

  上文代码的例子中,让asc=null是在终结器中完成的,实际工作中,一旦我们感觉到自己的静态引用类型参数占用内存空间比较大,并且使用完毕后不再使用,则可以立刻将其赋值为null。这也许并不必要,但这绝对是一个好习惯。试想一下在一个大系统中,那些时不时在类型中出现的静态变量吧,它们就那样静静地呆在内存里,一旦被创建,就永远不离开,越来越多,越来越多。

时间: 2024-09-20 16:41:57

一起谈.NET技术,引用类型赋值为null与加速垃圾回收的相关文章

改善C#程序的建议5:引用类型赋值为null与加速垃圾回收

原文:改善C#程序的建议5:引用类型赋值为null与加速垃圾回收 在标准的Dispose模式中(见前一篇博客"C#中标准Dispose模式的实现"),提到了需要及时释放资源,却并没有进一步细说让引用等于null是否有必要. 有一些人认为等于null可以帮助垃圾回收机制早点发现并标识对象是垃圾.其他人则认为这没有任何帮助.是否赋值为null的问题首先在方法的内部被人提起.现在,为了更好的阐述提出的问题,我们来撰写一个Winform窗体应用程序.如下: privatevoid button

引用类型赋值“.NET技术”为null与加速垃圾回收

在标准的Dispose模式中,提到了需要及时释放资源,却并没有进一步细说让引用等于null是否有必要. 有一些人认为等于null可以帮助垃圾回收机制早点发现并标识对象是垃圾.其他人则认为这没有任何帮助.是否赋值为null的问题首先在方法的内部被人提起.现在,为了更好的阐述提出的问题,我们来撰写一个Winform窗体应用程序.如下: private void button1_Click(object sender, EventArgs e) { Method1(); Method2(); } pr

SQL Server误区30日谈 第18天 有关FileStream的存储,垃圾回收以及其它_MsSql

误区 #18:如下多个有关FileStream的误区 全部错误 18 a)FileStream数据可以在远程存储     不能,由于FileStream数据容器(指的是存放FileStream文件的NTFS文件夹,杜撰出来的术语)必须像数据文件或日志文件那样符合本地存储策略-也就是说,这个数据容器必须放在对于运行SQL Server的Windows Server是本地存储(译者注:也就是在'计算机'里能看到的存储,DAC当然是了,其实SAN这类不直接连接服务器的也算是)访问FileStream数

一起谈.NET技术,关于ASP.NET页面打印技术的总结

B/S结构导致了Web应用程序中打印的特殊性. • 程序运行在浏览器中,打印机在本地,而文件确可能在服务器上,导致了打印控制不是很灵活. • 格式如何控制和定制等,是我们开发中可能会面对的问题. 打印文档的生成 • 1.客户端脚本方式 一般情况下,主要使用JS 可以分析源页面的内容,将欲打印的页面元素提取出来,实现打印.通过分析源文档的内容,可以生成打印目标文档. 优点:客户端独立完成打印目标文档的生成,减轻服务器负荷; 缺点:源文档的分析操作复杂,并且源文档中的打印内容要有约定. • 2.服务

关于将临时变量置为null是否有助于快速垃圾回收

"将不再使用的临时变量立即置为null是否有助于垃圾回收"的话题好像有不少人争论过. 首先,我们要理解GC回收垃圾数据的标准是通过路径检查,看是否还有引用指向某个对象.如果不再有引用指向这个待回收的对象,那么GC会将其放入待回收队列.从这个理论上来说,适时将不再使用的变量置为null是有助于垃圾回收的.但是,进一步理解GC的工作模式,我们会发现如下几个问题. 1. GC只是将其置于待回收队列,并不一定立即回收(需要一定的条件).2. GC并不会在变量置为null时启动回收动作. 所以只

一起谈.NET技术,《Effective C#中文版:改善C#程序的50种方法》读书笔记

从去年找工作以来,都没什么时间写博客[找工作的体会:建议以后有自己开公司的IT人一定要找IT专业人员做HR,好多公司的HR并不能真正发掘人才,他们形成了太多的偏见,如在学校期间学不了什么东西.只看学校有多少奖励等.真正钻研技术的人才不会追求虚无的东西],其实这本书我都借了好久,一直没有系统的看,所以趁这两天好好看看,顺便总结了一些要点,给那些需要这方面知识而又没有太多时间的IT人一个快速的学习机会....如果要深入学习,请购买该书. 一.用属性代替可访问的字段 1..NET数据绑定只支持数据绑定

一起谈.NET技术,性能优化总结

最近在领导的要求下做了一下项目的优化,总结如下: 1. 使用存储过程(如果在程序里用exec 存储过程参数,这样执行似乎并没有快多少). 在数据库里是预编译的,也不需要在字符串传输上花费大量时间. 防sql注入攻击. 2. 尽量优化数据库语句,使逻辑尽量简单       a) 还有就是在使用函数时,charindex >like > padindex  效率依次递减. b) 查询字段是否包含在以,分隔的字段串时,最好不要用in  速度非常慢. 还有好多,可以总结的,这里就不再描述了. 3. 

一起谈.NET技术,关于CLR内存管理一些深层次的讨论 [下篇]

<上篇>中我们主要讨论的是程序集(Assembly)和应用程序域(AppDomain)的话题,着重介绍了两个不同的程序集加载方式--独占方式和共享方式(中立域方式):以及基于进程范围内的字符串驻留.这篇将关注点放在托管对象创建时内存的分配和对大对象(LO:Large Object)的回收上,不对之处,还望各位能够及时指出. 一.从类型(Type)与实例(Instance)谈起 在面向对象的世界中,类型和实例是两个核心的要素.不论是类型和实例,相关的信息比如加载到内存中,对应着某一块或者多块连续

javase-io流变量在new前,为什额还需要赋值为null。

问题描述 io流变量在new前,为什额还需要赋值为null. fileinputstream in=null; try{ in=new fileinputstream(f);}catch{}finally{if(in!=null) in.close();}定义变量in时,为什么还需要赋值为null:这里的概念不太清楚. 又想起了,有些时候定义的基本类型的变量eclipse也提示需要赋一个初始值, 解决方案 这是因为JAVA要求在使用变量之前必须要初始化,JAVA不像C/C++一样,在定义局部变量