zoukankan      html  css  js  c++  java
  • 关于 Linq to EF 的内存泄漏问题

    查到一些解决方案:          

     

    1, http://www.codethinked.com/keep-your-iqueryable-in-check

    自定义常用方法,屏蔽IQuery功能。这个好像有点靠谱。但麻烦。

     

    2,http://stackoverflow.com/questions/123057/how-do-i-avoid-a-memory-leak-with-linq-to-sql

    这个看起来是应对 ObjectContext的。

     

    3,http://stackoverflow.com/questions/19116851/entity-framework-using-repository-pattern-unit-of-work-and-unity

    这个看起来简单些 ,用using.

     

    4, 直接用using的方式

    using(dbcontext)

    {

    }

     

    5, http://blog.robustsoftware.co.uk/2008/11/clearing-cache-of-linq-to-sql.html

    这个针对 DataContext的一个扩展方法。

     

     

    最后没有更好的解决办法。

     

     

    /******************************Edit 2014-1-25**************************************************************/

     

    最后在分析了DbContext的源代码以后,发现了其中的奥秘。

    首先DbContext本身是对于ObjectContext的一个包装,在使用IQueryable以后会将Enitiy存于 objectStateManager ,在快速的循环中,_unchangedEntityStore不会被释放,一直被缓存,所以呢?你会发现程序的内存在不断的上涨,这不应该算是内存泄漏。 由于 _unchangedEntityStore 中的值变得越来越大,程序在查询前先去 _unchangedEntityStore 中查找,由于Dictionary<EntityKey, EntityEntry> 在很大的情况下,查询性能会急剧下降,所以程序变慢。 直到慢得你无法忍受。

     

    下面是基本代码。

     

    public class Class1
     {
         public static void Test()
         {
             Domain.Entities.Models.passportContext context =
                 new Domain.Entities.Models.passportContext("passportContext");
             context.Configuration.ProxyCreationEnabled = false;
             context.Configuration.LazyLoadingEnabled = false;
     
             var start = int.MaxValue;
     
     
             var om = ((IObjectContextAdapter)context).ObjectContext.ObjectStateManager;
     
             om.ObjectStateManagerChanged += om_ObjectStateManagerChanged;
     
             while (true)
             {
                 var list = context.UserSources.OrderByDescending(x => x.UserId).Where(x => x.UserId < start).Take(100).ToList();
     
     
     
     
                 ObjectContext oc = ((IObjectContextAdapter)context).ObjectContext;
     
                 var m = oc.ObjectStateManager;
                 var a = list.First();
     
                 var count = GetUnchangedCount(context);
     
                 var mth = m.GetType().GetMethod("GetObjectStateEntriesCount", BindingFlags.Instance | BindingFlags.NonPublic);
     
                 var unchanged = mth.Invoke(m, new object[] { EntityState.Unchanged });
     
                 if (a.UserId % 4 == 0)
                 {
                     a.FromSite += 1;
                     context.Entry(a).State = System.Data.EntityState.Modified;
                 }
     
     
                 count = GetUnchangedCount(context);
     
                 ClearUnchangedCache(context);
     
                 Console.WriteLine(a.UserId);
     
                 //ClearCache(context);
                 start = a.UserId;
             }
     
         }
     
         static void om_ObjectStateManagerChanged(object sender, System.ComponentModel.CollectionChangeEventArgs e)
         {
             Console.WriteLine("[]" + e.Action + "" + e.Element.GetType() + "::" + e.Element);
         }
     
     
         public static void ClearCache(DbContext context)
         {
             ObjectContext oc = DbContext2ObjectContext(context);
     
             const BindingFlags flags = BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance;
     
             var c = oc.GetType().GetField("_cache", flags);
     
             //var value = c.GetValue(oc);
     
             c.SetValue(oc, null);
     
             //value = c.GetValue(oc);
         }
     
     
         static int GetUnchangedCount(DbContext context)
         {
     
             var objectStateManager = DbContext2ObjectStateManager(context);
     
             var mth = objectStateManager.GetType().GetMethod("GetObjectStateEntriesCount", BindingFlags.Instance | BindingFlags.NonPublic);
     
             var unchanged = mth.Invoke(objectStateManager, new object[] { EntityState.Unchanged });
     
             return (int)unchanged;
         }
     
     
         static ObjectContext DbContext2ObjectContext(DbContext context)
         {
             return ((IObjectContextAdapter)context).ObjectContext;
         }
     
         static ObjectStateManager DbContext2ObjectStateManager(DbContext context)
         {
             return DbContext2ObjectContext(context).ObjectStateManager;
         }
     
         static void ClearUnchangedCache(DbContext context)
         {
             var objectStateManager = DbContext2ObjectStateManager(context);
     
             const BindingFlags flags = BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance;
     
             var c = objectStateManager.GetType().GetField("_unchangedEntityStore", flags);
     
             c.SetValue(objectStateManager, null);
         }
     
     }

     

     

    然后发现内存还是疯长。

    image

     

    那就再加一个方法。

    static void ClearKeylessEntityCache(DbContext context)
           {
               var objectStateManager = DbContext2ObjectStateManager(context);
     
               const BindingFlags flags = BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance;
     
               var c = objectStateManager.GetType().GetField("_keylessEntityStore", flags);
     
               c.SetValue(objectStateManager, null);
           }

     

     

    再看内存,

    image

     

    这样世界顿时安静了下来。

     

     

     

     

     

    最后说明:

    这个方法的确不是一个好的办法,但是由于EF的对象管理机制决定。 人家缓存也有人家缓存的道理。

    实际上MS也没有打算让你按这样的方法来用,

    用using(dbcontext)

    {

    …..

    }

    这样的方式反而是保持了 Kiss。

     

     

    对于这样的“改进” 会不会有什么影响呢?

    这个就再开个文章再来测试了。

  • 相关阅读:
    Docker 容器相关命令
    Docker 镜像相关命令
    Docker 守护进程相关命令
    VMware Workstation 报w32authconnectionlaunch:readfile失败
    关于 Vue Baidu Map 自动定位
    前端面试题第一天
    记录一下使用element ui使用级联选择器的坑,级联选择器的默认选中
    判断值得类型,以及判断对象是否为空对象
    js计算两个时间差
    关于axios请求携带cookie以及封装
  • 原文地址:https://www.cnblogs.com/zbw911/p/3532793.html
Copyright © 2011-2022 走看看