MVC+MEF+UnitOfWork+EF架构,网站速度慢的缘由总结!(附加ANTS Memory Profiler简单用法)

(最近使用内存分析工具ANTS Memory Profiler,以及其余网友提供的意见发现最终致使内存泄漏的就是MEF,在此特意更新下,与你们分享!最下面红色字体)html

最近参考使用了郭明峰的一套架构来作新的项目架构,这套架构看起来仍是不错的,先向小郭同窗的分享精神致敬!sql

郭同窗的项目文档:http://www.cnblogs.com/guomingfeng/archive/2013/05/19/mvc-overall-design.html架构

项目开发上线后,傻眼了,貌似没有几我的访问的新项目,速度一直慢的跟牛同样,真心无法交差啊。上面发下话了,解决不了就能够走人了。压力可想而知。mvc

接下来就是苦逼的找缘由了。工具

症状:一、内存占用高,8g的内存很快就能吃完性能

         二、网站相应速度慢,firebug检测每次都是在等待学习

根据这些症状,感受是内存泄露了,因而memory profiler。。。什么性能分析工具都用上了,怎奈水平有限,不会使用这些高级玩意,一番折腾下来,就剩下无奈。测试

但总不能放弃,对比以前作的mvc项目,就是多了entityframework、MEF这两样,看样子也就这两块的缘由了,EF的嫌疑最大,并且首次使用,并不了解。因而,接下来又是一段苦逼的研究;功夫不负有心人,根据目前研究的结果及上线的效果来看,基本上找到了问题的所在。总结下,与诸位分享,高手忽略。字体

问题缘由:一、不少数据查询一次性读入到内存中,致使内存增长。优化

               二、上下文对读入到内存中的数据对象会进行跟踪,致使内存不断攀升,疑似内存泄露

解决办法:一、深刻学习EF,监控每一步生成的sql语句,防止无用数据的调取

               二、网站大部分的数据在读取的时候是不须要进行上下文跟踪的,必定要禁止跟踪,不然内存就会爆了!代码以下:

/// <summary>
        /// 获取当前实体的查询数据集(经过读上下文进行读取,只读专用,返回的实体数据不会被上下文跟踪)
        /// </summary>
        public virtual IQueryable<TEntity> ReadEntities
        {
            get { return WriteContext.Set<TEntity>().AsNoTracking(); }
        }

        /// <summary>
        /// 获取当前实体的查询数据集(经过写上下文进行读取,修改专用,返回的实体数据会被上下文进行跟踪)
        /// </summary>
        public virtual IQueryable<TEntity> WriteEntities
        {
            get { return WriteContext.Set<TEntity>(); }
        }

说明:我在仓储模式中,对读取实体集合进行了修改,ReadEntities表示不须要进行跟踪的,加上AsNoTracking方法;WriteEntities表示是须要跟踪的,用于修改实体信息使用。这样以来,全部的症状所有消失了。

固然,也许有更好的优化方法,至少目前本人发现了这些问题所在,并且微软的白皮书中确实也讲的了EF的性能注意事项,怎奈仓促开发,对EF绝不了解,罪过罪过!你们引觉得鉴!

 

另外,还遇到一个问题,不知道是不是DBcontext线程冲突问题,你们帮分析下,谢谢:

 

    持续的内存泄漏问题终于有告终果了,花了近一周的时间,无数苦逼的熬夜,终于掌握了ANTS Memory Profiler的基本用法,结合以前网友的宝贵意见,经检测,终于发现致使内存泄漏的罪魁祸首:MEF。

小郭的架构中有这么一段代码:

        public CompositionContainer Container
        {
            get
            {
                CompositionContainer container;
                if (!HttpContext.Current.Items.Contains(MefContainerKey))
                {
                    container = new CompositionContainer(_catalog, CompositionOptions.DisableSilentRejection);
                    HttpContext.Current.Items.Add(MefContainerKey, container);
HttpContext.Current.DisposeOnPipelineCompleted(container); }
else { container = (CompositionContainer)HttpContext.Current.Items[MefContainerKey]; } return container; } }

这段代码用于建立MEF容器,并向其中加入对象实例化的目录。

小郭童鞋说了,项目中的全部Import的对象都是MEF建立的,不须要刻意的去Dispose,只要MEF对象释放了,那么container中的全部实例化对象就会被释放。

话没错,但代码中确实没有看到有释放container对象的地方,我翻遍了评论,看到有个哥们ltcszk提醒说要加入HttpContext.Current.DisposeOnPipelineCompleted(container)这句话就ok,当时并未重视,执拗的认为是过多的DBContext致使的,这两天通过ANTS Memory Profiler测试下,果然是这个问题致使的,加上这句话,效果立竿见影,在此感谢ltcszk的提醒。同时总结下,与诸位分享!

 

在页面打开后,理论上此次请求就完毕,全部线程中建立的对象就应该被GC释放掉,Profiler在每次快照的时候就会执行GC的回收,那么实际的状况咱们看下

优化前:

内存情况:

第一步

第二步

第三步

 

优化后:

驻留在内存中的类型明显少了不少,只有35个,大部分是cache内容!

在此记录,与诸位分享,欢迎拍砖!

相关文章
相关标签/搜索