如何释放C#中List分配的内存

时间:2017-05-25 12:32:06

标签: c# list memory-management out-of-memory dispose

我有这样的代码:

var myList = db.Table1.ToList();
/*doing some operations on the list*/
var myList = db.Table2.ToList();
/*again doing some operations on the list*/
var myList = db.Table3.ToList(); // I'm getting out of memory exception here.

我无法按页检索数据,因为我需要一次所有表。在加载另一个表之前,如何处理(我的意思是释放该列表所公开的空间)列表?感谢。

编辑:

我加载后,实际上是从myList生成了很多(有时是数千个)子列表。所以我真的需要学习如何释放列表。

编辑2: 这是我的完整Stacktrace:

   at System.Collections.Generic.List`1.set_Capacity(Int32 value)
   at System.Collections.Generic.List`1.EnsureCapacity(Int32 min)
   at System.Collections.Generic.List`1.Add(T item)
   at System.Data.Entity.Core.Objects.EntityEntry.TakeSnapshot(Boolean onlySnapshotComplexProperties)
   at System.Data.Entity.Core.Objects.Internal.SnapshotChangeTrackingStrategy.TakeSnapshot(EntityEntry entry)
   at System.Data.Entity.Core.Objects.Internal.EntityWrapper`1.TakeSnapshot(EntityEntry entry)
   at System.Data.Entity.Core.Objects.ObjectStateManager.AddEntry(IEntityWrapper wrappedObject, EntityKey passedKey, EntitySet entitySet, String argumentName, Boolean isAdded)
   at System.Data.Entity.Core.Common.Internal.Materialization.Shaper.HandleEntityAppendOnly[TEntity](Func`2 constructEntityDelegate, EntityKey entityKey, EntitySet entitySet)
   at System.Data.Entity.Core.Common.Internal.Materialization.Coordinator`1.ReadNextElement(Shaper shaper)
   at System.Data.Entity.Core.Common.Internal.Materialization.Shaper`1.SimpleEnumerator.MoveNext()
   at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
   at System.Collections.Generic.List`1.InsertRange(Int32 index, IEnumerable`1 collection)
   at System.Collections.Generic.List`1.AddRange(IEnumerable`1 collection)
   at WebApplication2.MyMethod in line 2292
   at WebApplication2.Controllers.MyController.MyActtion(String myString) in line 137
   at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
   at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<BeginInvokeSynchronousActionMethod>b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3d()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f()

2 个答案:

答案 0 :(得分:5)

根据帖子评论中的其他说明,db是3个调用之间的EF DbContext派生类变量,共享Table1Table2Table3是该上下文中的DbSet个实例。

使用var myList = db.Table1.ToList()时会发生的情况是,除了在内存中读取表格并填写列表之外,EF DbContext还会使用相同的对象填充内部DbSet.Local集合(所谓的 tracking ),以便能够检测并应用您(最终)对底层对象所做的更改。

这就是问题所在。如果您Clear设置为null myList变量,内部缓存(列表)仍保留所有这些对象,最终会在某个时刻导致OutOfMemoryException,则会发生事件。

因此,如果您不打算修改返回的对象并将它们保存回数据库,那么通过使用所谓的No-Tracking Queries来完全消除DbContext内部缓存:

var myList = db.Table1.AsNoTracking().ToList();

或清理(Dispose并设置为null)并为每个列表流程使用全新的上下文(db = new YourDbContext())。

当然,您可以将上述内容与评论中提到的其他技术结合起来,以清理您自己的列表变量。

答案 1 :(得分:0)

根据我上面的评论,ToList在处理大量项目时会对内存造成严重影响。

因此,通过预先分配List,有时可以获得更好的内存性能。

所以:

var tableCount = db.Table1.Count();
var myList = new List<Table1Item>(tableCount); //I don't know the type parameter here
myList.AddRange(db.Table1); //or db.Table1.AsEnumerable() ?