导航
  • 全部
  • C#综合技术
  • C#互联网桌面应用
  • AppLauncher
  • WinForm&WPF
  • C#开发新技术
  • 博文收录
  • Ada助手
  • 问答

obj=null;是否能让对象立刻释放,是否能加快对象释放,是否有必要加上?

ojekleen 2009-04-08 01:17:55
加精
我这个喜欢闭门造车,所以很多论点都有可能是错误,于是放在这里和大家一起商讨一下。如果对各位造成什么误导的话,个人不请求原谅,你们爱报复就报复,爱打击就打击,我不嫌弃臭鸡蛋,当然更喜欢鲜花。。。好了,客套就到此为至。以下说说主题。

昨天和一群友讨论内存回收的问题。突然有位自称IBM+移动的JAVA群友说在使用完对象后整个=null;这样对象可以加快翻译。。。并且经过IBM小组的严格测试。。。
汗一把,怀疑是难免的。后来看到坛子里也有讨论析构函数的,于是也发个帖。。

首先以,是否能让对象内存立刻回收 展开说法。。

个人认为这是错误的:
设置对象null时执行,设置对象null只是段开了内存对象与引用句柄之间的引用关系,只是把引用转了个位,并没有让对象为null
翻译对象主分两块,一为非托管对象,二为托管对象。
先以非托管对象为例:这个我简单测试为"设置对象null只是段开了内存对象与引用句柄之间的引用关系"是正确的。。
测试Code

static void OpenConnection(SqlConnection conn)
{
if (conn.State != ConnectionState.Open)
conn.Open();
}

static void TestUnManager()
{
try
{
for (int i = 0; i < 10; i++)
{
SqlConnection conn = new SqlConnection("server=20080827-1517; User ID=sa;Password=cherry; database=InforSys; min pool size=2; max pool size=3");

TimeSpan bts = DateTime.Now.TimeOfDay;
OpenConnection(conn);
TimeSpan ets = DateTime.Now.TimeOfDay;
TimeSpan ts = bts - ets;
Console.WriteLine(ts);
//conn.Close();
conn = null;
Thread.Sleep(1000);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}


基本来说,如果按照对象为null 的话,连接肯定是不存在的。实事是,连接在请求第5个的时候出exception,抛链接池已满。。


托管资源是否为一样?其实这个一般比较难测试,因为垃圾回收机制一般在内存进缺的时候会比较勤快,而且在申请内存方面也是很麻烦,
于是测试环境我为所有能关的服务都关了,内存为2G当前使用情况为581M的情况下测试,这样或许不会让垃圾回收器觉醒过来。。
但是内存申请也是个麻烦事,一开始使用StringBuilder来划内存,当然,效果肯定是没有的,后来只有采用粗鲁的手段从数据库大表里填充数据集。。这方法其实也很默哀,如果有更好方法的朋友可以告诉偶一下。。。。

测试CODE



static void TestManager()
{
for (int i = 0; i < 10; i++)
{
GetTable();
}
}

static void GetTable()
{

using (SqlConnection conn = new SqlConnection("server=20080827-1517; User ID=sa;Password=cherry; database=InfoSys; min pool size=20; max pool size=100"))
{
SqlCommand cmd = new SqlCommand("select * from gtl_comp", conn);
using (SqlDataAdapter da = new SqlDataAdapter())
{
DataTable dt = new DataTable();
da.SelectCommand = cmd;
da.Fill(dt);

dt = null;//这分两个,一个有,一个没有
}
}

}


分两种情况都执行了一下,发现内存的消耗情况基本相同,有dt=null的有时反而会多占用一点。。。
基本认为有没有是一个样,不能立刻对象释放。。(同在内存使用量不是很大的情况下)


以上测试比较简单,但我个人认为=null;是不能加快对象释放的。


最后一个为,是否有必要加上,受一坛子的帖启发来测试的。

MSDN原话:

垃圾回收器使用名为“终止队列”的内部结构跟踪具有 Finalize 方法的对象。每次您的应用程序创建具有 Finalize 方法的对象时,垃圾回收器都在终止队列中放置一个指向该对象的项。托管堆中所有需要在垃圾回收器回收其内存之前调用它们的终止代码的对象都在终止队列中含有项。



关键的是垃圾回收器在运行的时候会释放怎么样的对象?在我的理解里一直为当该对象没有任何引用时,垃圾回收器就运行该对象的析构,释放对象。。。

测试CODE

public class Test
{
private string name;
public Test(string name)
{
this.name = name;
StringBuilder sb = new StringBuilder(10240000);
for (int i = 0; i < 102400; i++)
sb.Append(name+i);
}

~Test()
{
Console.WriteLine(name+"执行到析构");
}

static void Main(string[] args)
{
//TestUnManager();
//TestManager();
NotSetNull();
SetNull();

}

static void NotSetNull()
{
for (int i = 0; i < 100; i++)
{
Test t = new Test("ojek"+i);
}
}

static void SetNull()
{
for (int i = 100; i < 200; i++)
{
Test t = new Test("ojek" + i);
t = null;
}
}
}




执行两个方法,都会输出相关内容。。即,t=null和不加上这句,析构都会运行。。。

运行结果还有一点特色,输出的并不是按顺序排例的(我这并没有启动线程),这个其实也证明了垃圾回收器在释放对象的时候是无序的,即,不能确保对象释放时利用组合模式,从内向外或者从外向内释放。。。



然后这个不免产生疑惑: 变量或者说引用,其实也是对象,如果引用没有释放的时候,引用指几的对象当然是不会被垃圾回收器释放的。如果垃圾回收器先找到对象,不释放,再找到引用,释放,这样对象就得等垃圾回收器下一次运行的时候再回收了。。。如果通过大数据的测试,或许可以证明,把引用和对象一刀两断,或许能够加快对象的释放。。





即obj=null;还是可要的,当然,如果是非托管对象要先dispose()再断开对象与引用之间的关系。。
...全文
给本帖投票
4841 221 打赏 收藏 转发到动态 举报
写回复
用AI写文章
221 条回复
切换为时间正序
请发表友善的回复…
发表回复
hm020 2009-04-23
  • 打赏
  • 举报
回复
好长的贴,占位学习.
Delta 2009-04-15
  • 打赏
  • 举报
回复
看到这个帖子,我还是是想说说。我那时学的时候,对这个垃圾回收器并不是很理解,是的,看了大家的看法,是有一些长进。微软件的东西,个人认为,越用越让人省心,这也同意楼上某一人说的那个意思。好多东西,还是要好好研究一下,虽然我还是个初级程序员。
rightyeah 2009-04-14
  • 打赏
  • 举报
回复
支持80楼的帖子。
object o = new object();
stmt1;
o.xxx();
stmt2;
stmt3;
o = null;

其实编译器在为o.xxx()后面就知道它(原来o指向的那个对象)是可回收的,
比你在两个语句后再o=null更准确!

对于局部变量,正常情况下确实不需要obj=null这样的用法。因为局部变量在它最后一次被访问(注意是对变量本身的读,对于变量引用的对象来说,可以是任何操作)后,它所引用的对象,就被系统认为是可回收的了(除非还有别的未超出范围的变量继续引用它)。在80楼的帖子中,o.xxx()后,对o所引用的对象,已经没有任何操作了,也没有任何别的对象引用它,系统会直接将视作可回收的对象了(当然,必须是在gc遍历对象的时候)。
对于局部变量来说,它更需要的可能是实现IDisposable接口,并在函数(指任何使用了局部变量的方法、事件、语句块)结束前调用Dispose方法,通知对象释放它占用的资源。这会比等待gc调用析构函数显得更为积极。
Mr-Jee 2009-04-14
  • 打赏
  • 举报
回复
补充下最后一个例子
没有obj=null的那个只调用到=98为止
而使用obj=null的那个调用到了最后一个
能证明什么我也不说了
guoyong1986 2009-04-14
  • 打赏
  • 举报
回复
可以实现IDisposable接口,自己手动释放对象。
MSDN上官方指导方法。
hangang7403 2009-04-14
  • 打赏
  • 举报
回复
up
madshime 2009-04-14
  • 打赏
  • 举报
回复
回帖是一种美德!每天回帖即可获得 10 分可用分!
龙宜坡 2009-04-13
  • 打赏
  • 举报
回复
LZ很强大!
tomiji 2009-04-13
  • 打赏
  • 举报
回复
机制的问题,很强大,膜拜了
tangbo0210 2009-04-13
  • 打赏
  • 举报
回复
不会加快释放资源。但是一个好习惯。呵呵
尤其是用到了COM的时候。你那样做回有很大的延迟。一般都是用GC来释放。
wenjie0728 2009-04-13
  • 打赏
  • 举报
回复
学习
yc_8301 2009-04-13
  • 打赏
  • 举报
回复
在C#中,对于=null的赋值还是有必要的,如楼主所说这样能 段开了内存对象与引用句柄之间的引用关系,
而当.net认为内存紧缺的时候,会进行垃圾回收,回收的对象是为“根”的对象,即:没有被任何对象使用的对象。。
一个没有被任何使用的对象,一般会经过2次垃圾回收处理才会被回收掉。。第一次回收是:进行“根”,检查,并且调用Finalize ,
且形成一个类似于链表的结构,第二次才会回收链表中的对象。。
以上,我可能说的不太清楚,有些我也记不太清楚了,,如果要知道详细,可以参考:
《.NET框架程序设计(修订版)》的第19章节。。

龙宜坡 2009-04-13
  • 打赏
  • 举报
回复
不过obj=null是个好的编码习惯!

龙宜坡 2009-04-13
  • 打赏
  • 举报
回复
是否能让对象所占用的空间立即释放?
不会立即释放!
GC做的事情吧!


能否加快对象释放?
不一定。
file.open()方法后再file=null会不会使file占用的资源立即释放?


有必要加上?
没必要!
如stream.close()方法后再stream=null;还有必要?



写与不写obj=null都不会立即释放对象占用的空间。

真正释放空间是GC做的事情:
我们只能用obj=null向GC报告这个对象不再使用了,而释放空间是GC的事情。

要看对这个"立即"怎么理解!

如果说要在执行完obj=null这句后对象所占用的空间已经被释放,我觉得比较难办,至少我认为比较难,除非你直接对内存进行操作!


个人看法,不对请指出!
readfuture 2009-04-12
  • 打赏
  • 举报
回复
学习
xzqttt 2009-04-12
  • 打赏
  • 举报
回复
在java中,obj=null之后只会符合垃圾回收算法中的一些规则,在下一次垃圾回收的时候,
可能会回收掉这个obj,也仅仅是可能而已.
niitnanfeng 2009-04-12
  • 打赏
  • 举报
回复
[Quote=引用 82 楼 ubiquitious 的回复:]
引用 71 楼 cppfaq 的回复:
太二了

我只能说你太愚蠢了,
我对.net的了解不比你少。
我用过的语言比你见过的都多。
你个SB,
井底之蛙。
.net让程序员变得越来越愚蠢。越来越弱智。
难怪待遇越来越低。。。
[/Quote]
哈哈,就这素质真丢我们中国人的脸,心胸太狭窄了!
tabbycat 2009-04-12
  • 打赏
  • 举报
回复
加 object obj = null; 的这种做法感觉没什么必要,至少让代码多出一行,增加出错概率

对于大多数的情况下,保持代码的简洁,更重要。

如果那个obj是一个很大的对象,可以把函数拆成两个,函数执行完毕,堆栈就释放了。GC会去回收那个obj的。

如果obj 是实现IDisposable的,可以使用using,这样更好一些。
DavidWu108 2009-04-12
  • 打赏
  • 举报
回复
1.有没有obj=null都会断开与对象的联系,所以本人认为没有必要加obj=null
2.析构没有必要最好不要用,按MSDN说法,实现的析构的对象在释放时要执行两次垃圾回收才可以释放
第一次是因为对象被终结器连接,不能被直接释放,在终结器工作后,第二次垃圾回收才能释放对象。

3.有另外的测试方法,
class test
{
static int counter=0;
public int Counter{
get{return counter;}
}
public test(){counter++;}

~test(){counter--;}
}
//对计数进行统计可以查看对象的创建与释放情况
xhdwell 2009-04-11
  • 打赏
  • 举报
回复
其实这个问题涉及到两个对象,一个是存放在堆栽中obj的对象引用,一个是存放在堆中obj对象的值,在执行obj=null的时候我们仅仅是将堆栽中的obj的对象引用的值变为了null值,而堆中存放的obj却没有发生变化。因此是不会回收的。
加载更多回复(201)

111,072

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧