创建大量对象时内存不足C#

我正在处理我的应用程序中的100万条记录,这是从MySQL数据库中检索的。 为此,我使用Linq获取记录并使用.Skip()和.Take()一次处理250条记录。 对于每个检索到的记录,我需要创建0到4个项目,然后将其添加到数据库中。 因此,必须创建的平均项目总数约为200万。

IQueryable objectCollection = dataContext.Repository(); int amountToSkip = 0; IList objects = objectCollection.Skip(amountToSkip).Take(250).ToList(); while (objects.Count != 0) { using (dataContext = new LinqToSqlContext(new DataContext())) { foreach (Object objectRecord in objects) { // Create 0 - 4 Random Items for (int i = 0; i < Random.Next(0, 4); i++) { Item item = new Item(); item.Id = Guid.NewGuid(); item.Object = objectRecord.Id; item.Created = DateTime.Now; item.Changed = DateTime.Now; dataContext.InsertOnSubmit(item); } } dataContext.SubmitChanges(); } amountToSkip += 250; objects = objectCollection.Skip(amountToSkip).Take(250).ToList(); } 

现在,在创建Items时出现问题。 运行应用程序( 甚至不使用dataContext )时,内存会持续增加。 这就像物品永远不会被处理掉。 有没有人注意到我做错了什么?

提前致谢!

好吧,我刚刚与我的一位同事讨论过这种情况,我们已经找到了以下可行的解决方案!

 int amountToSkip = 0; var finished = false; while (!finished) { using (var dataContext = new LinqToSqlContext(new DataContext())) { var objects = dataContext.Repository().Skip(amountToSkip).Take(250).ToList(); if (objects.Count == 0) finished = true; else { foreach (Object object in objects) { // Create 0 - 4 Random Items for (int i = 0; i < Random.Next(0, 4); i++) { Item item = new Item(); item.Id = Guid.NewGuid(); item.Object = object.Id; item.Created = DateTime.Now; item.Changed = DateTime.Now; dataContext.InsertOnSubmit(item); } } dataContext.SubmitChanges(); } // Cumulate amountToSkip with processAmount so we don't go over the same Items again amountToSkip += processAmount; } } 

通过这个实现,我们每次都配置Skip()和Take()缓存,因此不会泄漏内存!

啊,好旧的InsertOnSubmit内存泄漏。 当我尝试使用LINQ to SQL从大型CVS文件加载数据时,我遇到过这种情况并且多次撞到墙上。 问题是即使在调用SubmitChangesDataContext仍继续跟踪使用InsertOnSubmit添加的所有对象。 解决方案是在一定数量的对象之后提交SubmitChanges ,然后为下一批创建一个新的DataContext 。 当旧的DataContext被垃圾收集时,它所跟踪的所有插入对象(以及您不再需要的)也是如此。

“可是等等!” 你说,“创建和处理许多DataContext会产生巨大的开销!”。 好吧,如果您创建单个数据库连接并将其传递给每个DataContext构造函数,那就不是了。 这样,始终保持与数据库的单个连接,而DataContext对象则是一个轻量级对象,表示一个小工作单元,应在完成后丢弃(在您的示例中,提交一定数量的记录)。

我最好的猜测是导致内存泄漏的IQueryable。 也许没有针对Take / Skip方法的MySQL的适当实现,它在内存中进行分页? 发生了奇怪的事情,但你的循环看起来很好。 所有引用都应该超出范围并收集垃圾..

好吧,是的。

因此,在该循环结束时,您将尝试在列表中包含200万个项目,不是吗? 在我看来答案是微不足道的:存储更少的项目或获得更多的内存。

– 编辑:

我可能读错了,我可能需要编译和测试它,但我现在不能这样做。 我会把它留在这里,但我可能是错的,我没有仔细审查它以确定,但是答案可能certificate是有用的,或者不是。 (通过downvote判断,我猜不是:P)

您是否尝试过像这样在循环外声明Item:

 IQueryable objectCollection = dataContext.Repository(); int amountToSkip = 0; IList objects = objectCollection.Skip(amountToSkip).Take(250).ToList(); Item item = null; while (objects.Count != 0) { using (dataContext = new LinqToSqlContext(new DataContext())) { foreach (Object objectRecord in objects) { // Create 0 - 4 Random Items for (int i = 0; i < Random.Next(0, 4); i++) { item = new Item(); item.Id = Guid.NewGuid(); item.Object = objectRecord.Id; item.Created = DateTime.Now; item.Changed = DateTime.Now; dataContext.InsertOnSubmit(item); } } dataContext.SubmitChanges(); } amountToSkip += 250; objects = objectCollection.Skip(amountToSkip).Take(250).ToList(); }