世纪大谎言?调用Dispose()后资源还是没释放? 究竟如何才能把使用完的DataSet占用的内存释放掉? 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 不是这样说的,Dispose是把对象从堆上释放,但堆在内存中还是保留分配的那块区域,所以不能从内存的变化看资源是否释放。 对,Dispose()是个世纪大谎言,以后不要用了。 ~恩~~建议楼主将程序中所有Dispose()删掉~~呵呵~~ 那如何才能把占用的内存回收?要是不能马上回收,那我一个系统(有很多DataSet之类的大对象)在程序运行一段时间后岂不是要占据几百兆内存了?(我用了十多个DataSet就占用了一百多兆). to lsbbox(凉薯): 那是不是说在调用Dispose()后对象所占据的内存就可以被系统重新分配给其他请求使用了? to lsbbox(凉薯) : 那是不是说在Dispose()后对象所占的内存就可以被系统重新分配给新的请求使用了? 那我在Dispose()后再调用对象的方法应该出错才是,可我在Dispose()后再调且对象其他方法,居然可以照常使用. 我怀疑Dispose()的定义是这样的:public void Dispose(){ //Do Nothing,haha...} 那如何才能把占用的内存回收?要是不能马上回收,那我一个系统(有很多DataSet之类的大对象)在程序运行一段时间后岂不是要占据几百兆内存了?(我用了十多个DataSet就占用了一百多兆).————————————————几百兆?你的程序有几十个DataSet? cherno(且歌且狂) :刚开始运行当然没那么多,但如果一个窗口有一个DataSet,填充记录后就占了十多兆,用户关掉这个窗口后内存还是没有释放,在程序运行一段时间后用户就重复打开关闭些窗口十多次了,这样就有一百多兆收不回了,一个系统有十几个这样的窗口的话,1G内存都有可能收不回了.(我测试才关闭窗口后,甚至再调用一次窗口的Dispose(),所占的内存也没有马上释放) Dispose()有时还有用,一次查找有没有没关闭的窗体时,用MdiChildren.Length总是不正确,明明都关了,总是时不时的!=0后来,以每个窗口关闭时,都来一次Dispose()完全搞定了。可能有点用吧。 错!!!!!Dispose方法本身并不释放堆中的对象资源,而是执行组件内部的Dispose方法的代码,同时释放引用该对象的地址指针,该对象需要在垃圾收集器收集时其资源才被释放。如下代码为例:Button btn=new Button();//use Buttonbtn.Dispose();GC.Collect();此时再看内存资源必定释放。调Dispose方法本身并不释放对象资源,对象资源只能由垃圾收集器释放,而垃圾收集器只在创建新对象时堆中资源不足才被触发,所以才会遇到好像资源没有被释放的问题。组件内的Dipose方法并没有调用垃圾收集器的收集资源方法。Dispose方法在开发组件时主要用于关闭组件内打开其他资源。 to chutzp(最水的dotnet专家): GC.Collect();此时再看内存资源必定释放。?____________________________________ 我测试很多次了,在Dispose()后调用GC.Collect()后程序使用的内存是有增无减(不说假话,真的是增加了),我一狠心在Dispose()后再来一个object=null,然后GC.Collect(),仍无效果 你判断一下堆地内存:GC.GetTotalMemory(true).ToString();其中true表示等待垃圾收集完毕后再获取其内存大小。如下两行代码肯定能把object资源释放:object.Dispose();GC.Collect();你看到的内存没有减小有可能是释放之前的内存状态,或者是Heap资源没有压缩时的状态。对象资源的释放和堆压缩是不同的。对象资源释放后堆并不一定立即压缩。 private void button1_Click(object sender, System.EventArgs e) { this.sqlConnection=new SqlConnection(); sqlConnection.ConnectionString="server=LearnH;uid=sa;pwd=123;database=test"; this.sqlCommand=new SqlCommand(); sqlCommand.CommandText="select * from test"; sqlCommand.Connection=sqlConnection; sqlDataAdapter=new SqlDataAdapter(); sqlDataAdapter.SelectCommand=sqlCommand; dataSet=new DataSet(); sqlDataAdapter.Fill(dataSet,"test"); Console.WriteLine(GC.GetTotalMemory(true).ToString()); }///////////////////////////////////////////////////private void button2_Click(object sender, System.EventArgs e) { Console.WriteLine(GC.GetTotalMemory(true).ToString()); dataSet.Dispose(); sqlConnection.Close(); sqlConnection.Dispose(); sqlDataAdapter.Dispose(); GC.Collect(); Console.WriteLine(GC.GetTotalMemory(true).ToString()); //MessageBox.Show(dataSet.Tables[0].ToString()); }///////////////////////////////Console中输出结果如下:260864264512264348 把button1_Click中的Console.WriteLine(GC.GetTotalMemor(true).ToString());注释掉则输出如下:260896264332 .NET 有一个垃圾回收器,就是将释放过的内存释放掉的,在帮助上有明确的说法,去 MS 的站点看也有这样的说法。 完全同意,我在Dispose方法定义了关闭database,结果系统运行为意提示连接池出错,后来发现Dispose方法中的DB.close()方法跟本无效,显式掉用DB.close()方法后正常! '///澄清: 无用单元收集器(GC)并不是经常运行的。它是定期运行的,根据一种复杂的测定计算机所做工作数量的算法,确定可能需要删除多少对象。GC运行时,它会浏览程序已创建的所有对象的列表,并删除可被删除的对象。 在以前传统的编程技术中,程序员们要负责删除他们自己的对象,并且有这样一种特权,可以告诉对象:“你,现在把自己从内存中清除出去!”有了.NET,他们就没有这种能力了,将来会在不确定的时候对对象进行删除。 确切地说,删除所发生的时间是不确定的,换言之,作为开发人员的你不会知道GC在什么时候会运行。这就意味着在删除对象的最后一个引用和从内存中实际删除该对象间并没有即时的联系,这就是所谓的不确定结束。 还找不到好的解决方法.Dispose()后GC.Collect()应该就把对象所占内存回收了,但是却没有把内存释放出来.而且频繁调用GC.Collect()对效率的影响更大. xumahua(xumahua): 同意你的说法,如果我们不显式调用GC.Collect()的话,我们是无法知道GC是什么时候清除对象的. 但是当我们显式调用GC.Collect(),GC就应该即时收集无用对象并释放内存,否则GC.Collect()对编程人员还有什么用? 是的,我们可以强迫GC去收集对象。但是即使我们调用了GC.Collect,也不会调用删除对象的最后引用和Finalize间的即时连接,它还是过一段时间才发生。总归,.NET已经剥夺了我们这个权利,它告诉我们:“你没有必要这样做,这是个不好的习惯!”:( 个人认为,垃圾收集器应做的工作应该是程序员忘记释放对象后的一种补偿机制.辟如说.NET的GC,GC所做的工作应该是检查程序中无用的、程序员又忘记释放的对象,然后释放之。 但是GC的工作不应该把释放对象的高效、方便的析构器的任务也抢过来了。在我们需要实时释放大的无用对象的时候,我们应该可以很方便地把对象析构,立即释放对象所占资源。 GC,只应作为一种补偿,而不是“强大”到不允许程序员即时释放资源。 http://www.aspcool.com/lanmu/browse1.asp?ID=992&bbsuser=vbnet "CLR垃圾回收器(CLR Garbage Collector)的主要任务就是监视程序使用的资源,当可用资源达到某个确定的极限时查找不再使用的对象,如发现有这类对象存在则释放它们所占用的资源。" 只不知"某个确定的极限"是什么时候?难道像caozi(浩子)说的那样要等到连"虚拟内存"都用完了才到这个"确定的极限"? 非常同意楼主说的"GC,只应作为一种补偿,而不是“强大”到不允许程序员即时释放资源。" 难道在"CLR Garbage Collector"机制下我们就无法即时回收资源了吗?非得让GC等到连虚拟内存都不够分配了之后才给我们释放? 哎。。Dispose是用来释放非托管资源的。例如数据库连接。你这个算什么释放啊?内存资源?内存是属于托管资源的。除了GC,没有任何一种方法能释放内存。 C#可以在unsafe代码里运用指针,不知道能不能在unsafe代码里直接释放资源(如DataSet)而不用GC劳神呢? 不能。unsafe下,只有两种方式使用内存:一:fixed,用来锁住对象不给GC释放。二:stackalloc,这在堆栈中分配内存。这些内存不属GC的管理范围,也不属于楼主的问题。虽然说是说unsafe,其实也够安全的了。 To:回复人: xumahua(xumahua) ( ) 信誉:100 无用单元收集器(GC)并不是经常运行的。它是定期运行的... ^^^^^^^^^^^^^^前面的代码产生的内存问题实际上是由于连接池自动启动的问题。 To:回复人: xumahua(xumahua) ( ) 信誉:100 定期运行???????每隔1分钟?5秒钟? 看如下代码,窗体加载时内存使用比较大,点击Button1后内存立即变小很多。这不就是被GC收集了吗?private void Form1_Load(object sender, System.EventArgs e) { DataSet[] ds=new DataSet[20]; for(int i=0; i<20; i++) { ds[i]=new DataSet(); ds[i].ReadXml(@"D:\test.xml"); //不是从数据库读取,直接从XML加载数据,这样就不会启用连接池了。//在连接数据库时,连接池在默认情况下自动启用,如果不需要连接池可以关闭之,详情请参见MSDN。 } this.textBox1.Text=GC.GetTotalMemory(true).ToString(); for(int i=0; i<20; i++) { ds[i].Dispose(); } this.textBox2.Text=GC.GetTotalMemory(true).ToString(); } private void button1_Click(object sender, System.EventArgs e) { GC.Collect(); this.textBox3.Text=GC.GetTotalMemory(true).ToString(); }注意:上面使用的是局部变量ds[]数组,如果这样定义:public class testGC{private DataSet[] ds;//////public void Form_Load(object sender, System.EventArgs e){ds=new DataSet[20];//...//...//...}}则垃圾收集器无法判断ds的资源是否真的为孤立对象,因为声明的作用域为当前类。此时,垃圾收集器无法回收其资源。而只有声明为作用域为当前方法内时,其资源才能真正释放。 好奇怪,星期天没想这玩意儿,到今天再测试一下(代码完全没改变),Dispose()再Collect()后内存明显减小.还有一件更奇怪的事,我点其中一个按钮的时候,忽然像死机了一样鼠标不能动了,过了一会程序所用内存竟然一下子减至四兆多,比启动时还少,可惜这样的事只发生了一次,以后再也试不到了(代码我一直没有改,我同事也在旁看着我测试的).不知这是不是GC的算法产生的结果还是D版的.NET IDE产生的结果? C#Winform中使用Panel切换的特效 如何快速判断FTP连接上,并且能判断密码是否正确 如何从一个xml文件中利用sql语句然后返回一个新的xml文件? 求一正则表达式 C# 字符串切分问题 请问ToolStripMenuItem.Image 属性如何设置? 请大家帮助:关于无线传输问题 急救 用XmlTextReader方式读取xml文件遇到点问题 急求usb口与设备通信代码 高分求知,在線等(解決立馬結帖,不夠分可再加)急急!!!!!!! 菜鸟的问题。
那是不是说在调用Dispose()后对象所占据的内存就可以被系统重新分配给其他请求使用了?
那是不是说在Dispose()后对象所占的内存就可以被系统重新分配给新的请求使用了?
那我在Dispose()后再调用对象的方法应该出错才是,可我在Dispose()后再调且对象其他方法,居然可以照常使用.
public void Dispose()
{
//Do Nothing,haha...
}
————————————————
几百兆?你的程序有几十个DataSet?
刚开始运行当然没那么多,但如果一个窗口有一个DataSet,填充记录后就占了十多兆,用户关掉这个窗口后内存还是没有释放,在程序运行一段时间后用户就重复打开关闭些窗口十多次了,这样就有一百多兆收不回了,一个系统有十几个这样的窗口的话,1G内存都有可能收不回了.
(我测试才关闭窗口后,甚至再调用一次窗口的Dispose(),所占的内存也没有马上释放)
后来,以每个窗口关闭时,都来一次Dispose()
完全搞定了。可能有点用吧。
Dispose方法本身并不释放堆中的对象资源,而是执行组件内部的Dispose方法的代码,同时释放引用该对象的地址指针,该对象需要在垃圾收集器收集时其资源才被释放。
如下代码为例:
Button btn=new Button();
//use Button
btn.Dispose();
GC.Collect();
此时再看内存资源必定释放。
调Dispose方法本身并不释放对象资源,对象资源只能由垃圾收集器释放,而垃圾收集器只在创建新对象时堆中资源不足才被触发,所以才会遇到好像资源没有被释放的问题。
组件内的Dipose方法并没有调用垃圾收集器的收集资源方法。
Dispose方法在开发组件时主要用于关闭组件内打开其他资源。
GC.Collect();
此时再看内存资源必定释放。?
____________________________________
我测试很多次了,在Dispose()后调用GC.Collect()后程序使用的内存是有增无减(不说假话,真的是增加了),我一狠心在Dispose()后再来一个object=null,然后GC.Collect(),仍无效果
GC.GetTotalMemory(true).ToString();
其中true表示等待垃圾收集完毕后再获取其内存大小。
如下两行代码肯定能把object资源释放:
object.Dispose();
GC.Collect();
你看到的内存没有减小有可能是释放之前的内存状态,或者是Heap资源没有压缩时的状态。对象资源的释放和堆压缩是不同的。
对象资源释放后堆并不一定立即压缩。
{
this.sqlConnection=new SqlConnection();
sqlConnection.ConnectionString="server=LearnH;uid=sa;pwd=123;database=test";
this.sqlCommand=new SqlCommand();
sqlCommand.CommandText="select * from test";
sqlCommand.Connection=sqlConnection;
sqlDataAdapter=new SqlDataAdapter();
sqlDataAdapter.SelectCommand=sqlCommand;
dataSet=new DataSet();
sqlDataAdapter.Fill(dataSet,"test");
Console.WriteLine(GC.GetTotalMemory(true).ToString());
}///////////////////////////////////////////////////
private void button2_Click(object sender, System.EventArgs e)
{
Console.WriteLine(GC.GetTotalMemory(true).ToString());
dataSet.Dispose();
sqlConnection.Close();
sqlConnection.Dispose();
sqlDataAdapter.Dispose();
GC.Collect();
Console.WriteLine(GC.GetTotalMemory(true).ToString());
//MessageBox.Show(dataSet.Tables[0].ToString());
}
///////////////////////////////
Console中输出结果如下:
260864
264512
264348
264332
无用单元收集器(GC)并不是经常运行的。它是定期运行的,根据一种复杂的测定计算机所做工作数量的算法,确定可能需要删除多少对象。GC运行时,它会浏览程序已创建的所有对象的列表,并删除可被删除的对象。
在以前传统的编程技术中,程序员们要负责删除他们自己的对象,并且有这样一种特权,可以告诉对象:“你,现在把自己从内存中清除出去!”有了.NET,他们就没有这种能力了,将来会在不确定的时候对对象进行删除。
确切地说,删除所发生的时间是不确定的,换言之,作为开发人员的你不会知道GC在什么时候会运行。这就意味着在删除对象的最后一个引用和从内存中实际删除该对象间并没有即时的联系,这就是所谓的不确定结束。
Dispose()后GC.Collect()应该就把对象所占内存回收了,但是却没有把内存释放出来.而且频繁调用GC.Collect()对效率的影响更大.
同意你的说法,如果我们不显式调用GC.Collect()的话,我们是无法知道GC是什么时候清除对象的.
但是当我们显式调用GC.Collect(),GC就应该即时收集无用对象并释放内存,否则GC.Collect()对编程人员还有什么用?
但是GC的工作不应该把释放对象的高效、方便的析构器的任务也抢过来了。在我们需要实时释放大的无用对象的时候,我们应该可以很方便地把对象析构,立即释放对象所占资源。
GC,只应作为一种补偿,而不是“强大”到不允许程序员即时释放资源。
只不知"某个确定的极限"是什么时候?难道像caozi(浩子)说的那样要等到连"虚拟内存"都用完了才到这个"确定的极限"?
非常同意楼主说的"GC,只应作为一种补偿,而不是“强大”到不允许程序员即时释放资源。"
你这个算什么释放啊?内存资源?
内存是属于托管资源的。
除了GC,没有任何一种方法能释放内存。
unsafe下,只有两种方式使用内存:一:fixed,用来锁住对象不给GC释放。
二:stackalloc,这在堆栈中分配内存。这些内存不属GC的管理范围,也不属于楼主的问题。虽然说是说unsafe,其实也够安全的了。
^^^^^^^^^^^^^^前面的代码产生的内存问题实际上是由于连接池自动启动的问题。
定期运行???????
每隔1分钟?5秒钟?
private void Form1_Load(object sender, System.EventArgs e)
{
DataSet[] ds=new DataSet[20];
for(int i=0; i<20; i++)
{
ds[i]=new DataSet();
ds[i].ReadXml(@"D:\test.xml"); //不是从数据库读取,直接从XML加载数据,这样就不会启用连接池了。
//在连接数据库时,连接池在默认情况下自动启用,如果不需要连接池可以关闭之,详情请参见MSDN。
}
this.textBox1.Text=GC.GetTotalMemory(true).ToString();
for(int i=0; i<20; i++)
{
ds[i].Dispose();
}
this.textBox2.Text=GC.GetTotalMemory(true).ToString();
} private void button1_Click(object sender, System.EventArgs e)
{
GC.Collect();
this.textBox3.Text=GC.GetTotalMemory(true).ToString();
}注意:
上面使用的是局部变量ds[]数组,如果这样定义:
public class testGC{
private DataSet[] ds;
//
//
//
public void Form_Load(object sender, System.EventArgs e)
{
ds=new DataSet[20];
//...
//...
//...
}
}
则垃圾收集器无法判断ds的资源是否真的为孤立对象,因为声明的作用域为当前类。此时,垃圾收集器无法回收其资源。而只有声明为作用域为当前方法内时,其资源才能真正释放。