依赖.NET自动垃圾收集器是一种不好的做法吗?
可以创建大量内存密集型对象,然后放弃对它们的引用。例如,我可能想从数据库下载并操作一些数据,我将进行100次单独的下载和处理迭代。我可以声明一个DataTable变量一次,对于每个查询,使用构造函数将其重置为一个新的DataTable对象,在内存中放弃旧的DataTable对象 DataTable类具有释放其使用的内存的简单内置方法,包括Rows.Clear()和.Dispose()。因此,在将变量设置为新的DataTable对象之前,我可以在每次迭代结束时执行此操作。或者我可以忘记它,让CLR垃圾收集器为我做这件事。垃圾收集器似乎非常有效,因此最终结果应该是相同的。当您不需要内存重的对象时,显式地处理它们是“更好的”(但添加代码来完成这一点),还是仅仅依靠垃圾收集器来为您完成所有工作(您受GC算法的支配,但您的代码更小) 根据要求,下面是说明回收数据表变量示例的代码:依赖.NET自动垃圾收集器是一种不好的做法吗?,.net,garbage-collection,.net,Garbage Collection,可以创建大量内存密集型对象,然后放弃对它们的引用。例如,我可能想从数据库下载并操作一些数据,我将进行100次单独的下载和处理迭代。我可以声明一个DataTable变量一次,对于每个查询,使用构造函数将其重置为一个新的DataTable对象,在内存中放弃旧的DataTable对象 DataTable类具有释放其使用的内存的简单内置方法,包括Rows.Clear()和.Dispose()。因此,在将变量设置为新的DataTable对象之前,我可以在每次迭代结束时执行此操作。或者我可以忘记它,让CLR
// queryList is list of 100 SELECT queries generated somewhere else.
// Each of them returns a million rows with 10 columns.
List<string> queryList = GetQueries(@"\\someserver\bunch-o-queries.txt");
DataTable workingTable;
using (OdbcConnection con = new OdbcConnection("a connection string")) {
using (OdbcDataAdapter adpt = new OdbcDataAdapter("", con)) {
foreach (string sql in queryList) {
workingTable = new DataTable(); // A new table is created. Previous one is abandoned
adpt.SelectCommand.CommandText = sql;
adpt.Fill(workingTable);
CalcRankingInfo(workingTable);
PushResultsToAnotherDatabase(workingTable);
// Here I could call workingTable.Dispose() or workingTable.Rows.Clear()
// or I could do nothing and hope the garbage collector cleans up my
// enormous DataTable automatically.
}
}
}
//queryList是在其他地方生成的100个SELECT查询的列表。
//它们每个都返回100万行10列。
List queryList=getquerys(@“\\someserver\bunch-o-querys.txt”);
数据表工作表;
使用(OdbcConnection con=newodbcconnection(“连接字符串”)){
使用(OdbcDataAdapter adpt=新OdbcDataAdapter(“,con)){
foreach(queryList中的字符串sql){
workingTable=new DataTable();//将创建一个新表。放弃上一个表
adpt.SelectCommand.CommandText=sql;
调整填充(工作台);
CalcRankingInfo(工作台);
将结果推送到另一个数据库(工作表);
//在这里,我可以调用workingTable.Dispose()或workingTable.Rows.Clear()
//或者我什么也做不了,希望垃圾收集器能清理我的垃圾
//自动生成庞大的数据表。
}
}
}
好的,是时候把事情弄清楚一点了(因为我原来的帖子有点混乱)
IDisposable与内存管理无关IDisposable
允许对象清除它可能持有的任何本机资源。如果对象实现了IDisposable
,则应确保在使用完块后使用或调用Dispose()
至于定义内存密集型对象,然后丢失对它们的引用,垃圾收集器就是这样工作的。这是件好事。让它发生,让垃圾收集器完成它的工作
…因此,要回答您的问题,不。依赖.NET垃圾收集器并不是一种不好的做法。事实恰恰相反。@Justin
…因此,要回答您的问题,不。依赖.NET垃圾收集器并不是一种不好的做法。事实恰恰相反
依靠GC为您清理是一种可怕的做法。不幸的是,你建议这样做。这样做很可能会导致内存泄漏,是的,在.NET中至少有22种方法可以“泄漏内存”。我曾在大量客户机上工作,诊断托管和非托管内存泄漏,为它们提供解决方案,并在多个.NET用户组上介绍了高级GC内部以及如何从GC和CLR内部进行内存管理
@作品:
应该对DataTable调用Dispose(),并在循环结束时显式将其设置为null。这会显式地告诉GC您已经完成了对它的操作,并且不再有对它的根引用。由于DataTable的大小很大,所以它被放置在LOH上。不这样做很容易分割LOH,导致OutOfMemoryException。记住,LOH永远不会被压实
有关更多详情,请参阅我在
@Henk-IDisposable和内存管理之间存在关系;IDisposable允许半显式地释放资源(如果正确实现)。资源总是有某种类型的托管和内存,通常是与之相关联的非托管内存
关于Dispose()和IDisposable,需要注意以下几点:
IDisposable提供了对托管和非托管内存的处置。非托管内存的处置应该在Dispose方法中完成,并且应该为IDisposable实现提供终结器
GC不会为您调用Dispose
如果不调用Dispose(),GC会将其发送到终结
队列,并最终再次到达f-可达队列。定稿
使对象在2个集合中生存,这意味着它将
如果在Gen0中,则升级到Gen1;如果在Gen1中,则升级到Gen2。
在您的例子中,对象位于LOH上,因此它会一直存在,直到完全恢复
GC(所有世代加上LOH)执行两次,
在“Health.NET”应用程序下,一个完整的集合是
每100个系列中约有1个。因为有很多
根据您的实现,LOH堆和GC上的压力是完全的
GC将更频繁地开火。这对性能是不可取的
因为完整GC需要更多的时间来完成。然后呢
它还依赖于您运行的GC类型以及
您正在使用LatencyModes(对此要非常小心)。即使
您正在运行后台GC(这已取代CLR中的并发GC)
4.0),临时集合(Gen0和Gen1)仍然阻塞/挂起线程。这意味着在此期间无法执行任何分配
时间您可以使用PerfMon来监视