如何释放由 C# 中的列表分配的内存
How to free memory which is allocated by a List in C#
我有这样的代码:
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.
我无法按页检索数据,因为我需要所有 table 一次。在加载另一个 table 之前,我如何处理(我的意思是释放由该列表分配的 space)列表?谢谢
编辑:
我实际上在加载 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()
根据我上面的评论,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() ?
根据 post 评论中的额外说明,db
是 EF DbContext
派生的 class 变量,shared 在 3 个调用之间。 Table1
、Table2
和 Table3
是该上下文中的 DbSet
个实例。
当您使用 var myList = db.Table1.ToList()
时会发生什么,除了读取内存中的 table 并填充您的列表之外,EF DbContext
还会填充内部 DbSet.Local
具有相同对象的集合(所谓的 tracking),以便能够检测并应用您(最终)对基础对象所做的更改。
这就是问题所在。如果您 Clear
事件,将 myList
变量设置为 null
,内部缓存(列表)仍然保存所有这些对象,最终在某个时候导致 OutOfMemoryException
。
因此,如果您不打算修改返回的对象并将它们保存回数据库,那么诀窍就是使用所谓的 No-Tracking Queries 完全消除 DbContext
内部缓存:
var myList = db.Table1.AsNoTracking().ToList();
或清理(Dispose
并设置为 null
)并为每个列表进程使用全新的上下文 (db = new YourDbContext()
)。
当然,您可以将上述方法与评论中提到的其他技术结合起来,以清理您自己的列表变量。
我有这样的代码:
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.
我无法按页检索数据,因为我需要所有 table 一次。在加载另一个 table 之前,我如何处理(我的意思是释放由该列表分配的 space)列表?谢谢
编辑:
我实际上在加载 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()
根据我上面的评论,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() ?
根据 post 评论中的额外说明,db
是 EF DbContext
派生的 class 变量,shared 在 3 个调用之间。 Table1
、Table2
和 Table3
是该上下文中的 DbSet
个实例。
当您使用 var myList = db.Table1.ToList()
时会发生什么,除了读取内存中的 table 并填充您的列表之外,EF DbContext
还会填充内部 DbSet.Local
具有相同对象的集合(所谓的 tracking),以便能够检测并应用您(最终)对基础对象所做的更改。
这就是问题所在。如果您 Clear
事件,将 myList
变量设置为 null
,内部缓存(列表)仍然保存所有这些对象,最终在某个时候导致 OutOfMemoryException
。
因此,如果您不打算修改返回的对象并将它们保存回数据库,那么诀窍就是使用所谓的 No-Tracking Queries 完全消除 DbContext
内部缓存:
var myList = db.Table1.AsNoTracking().ToList();
或清理(Dispose
并设置为 null
)并为每个列表进程使用全新的上下文 (db = new YourDbContext()
)。
当然,您可以将上述方法与评论中提到的其他技术结合起来,以清理您自己的列表变量。