MVC+MEF+UnitOfWork+EF架構,網站速度慢的原因總結!(附加ANTS Memory Profiler簡單用法)


(最近使用內存分析工具ANTS Memory Profiler,以及其他網友提供的意見發現最終導致內存泄漏的就是MEF,在此特地更新下,與大家分享!最下面紅色字體)

最近參考使用了郭明峰的一套架構來做新的項目架構,這套架構看起來還是不錯的,先向小郭同學的分享精神致敬!

郭同學的項目文檔:http://www.cnblogs.com/guomingfeng/archive/2013/05/19/mvc-overall-design.html

項目開發上線后,傻眼了,貌似沒有幾個人訪問的新項目,速度一直慢的跟牛一樣,真心沒法交差啊。上面發下話了,解決不了就可以走人了。壓力可想而知。

接下來就是苦逼的找原因了。

症狀:1、內存占用高,8g的內存很快就能吃完

         2、網站相應速度慢,firebug檢測每次都是在等待

根據這些症狀,感覺是內存泄露了,於是memory profiler。。。什么性能分析工具都用上了,怎奈水平有限,不會使用這些高級玩意,一番折騰下來,就剩下無奈。

但總不能放棄,對比之前做的mvc項目,就是多了entityframework、MEF這兩樣,看樣子也就這兩塊的原因了,EF的嫌疑最大,而且首次使用,並不了解。於是,接下來又是一段苦逼的研究;功夫不負有心人,根據目前研究的結果及上線的效果來看,基本上找到了問題的所在。總結下,與諸位分享,高手忽略。

問題原因:1、很多數據查詢一次性讀入到內存中,導致內存增加。

               2、上下文對讀入到內存中的數據對象會進行跟蹤,導致內存不斷攀升,疑似內存泄露

解決辦法:1、深入學習EF,監控每一步生成的sql語句,防止無用數據的調取

               2、網站大部分的數據在讀取的時候是不需要進行上下文跟蹤的,一定要禁止跟蹤,否則內存就會爆了!代碼如下:

/// <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內容!

在此記錄,與諸位分享,歡迎拍磚!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM