簡體   English   中英

C#Entity Framework高內存使用率,內存泄漏?

[英]C# Entity Framework high memory usage, memory leak?

我有一個使用Entity Framework 6運行的小型MVC web應用程序。通過瀏覽我的dev上的主頁(例如www.mywebsite.dev)啟動應用程序。 機器應用程序池啟動並按預期加載頁面。

鄙視這樣一個事實,即主頁非常輕量級,只能從數據庫中獲取一些東西(2個菜單,2個帶文本的段落,以及帶有3-4個對象的集合),應用程序池在加載后已經超過200 MB(!)主頁一次..

使用這個這個文章中,我已經設法弄清楚如何配置文件管理存儲,而且我也刪除了幾個靜態屬性阻斷支配的上下文。 DbContext禁用了延遲加載,

public class MyContext: DbContext
    {
        private readonly Dictionary<Type, EntitySetBase> _mappingCache = new Dictionary<Type, EntitySetBase>();

        #region dbset properties
        //Membership sets
        public IDbSet<UserProfile> UserProfiles { get; set; }
        public IDbSet<Project> Project { get; set; }
        public IDbSet<Portfolio> Portfolio { get; set; }
        public IDbSet<Menu> Menu { get; set; }
        public IDbSet<MenuItem> MenuItem { get; set; }
        public IDbSet<Page> Page { get; set; }
        public IDbSet<Component> Component { get; set; }
        public IDbSet<ComponentType> ComponentType { get; set; }
        public IDbSet<BlogCategory> BlogCategory { get; set; }
        public IDbSet<Blog> Blog { get; set; }
        public IDbSet<Caroussel> Carousel { get; set; }
        public IDbSet<CarouselItem> CarouselItem { get; set; }
        public IDbSet<Redirect> Redirect { get; set; }
        public IDbSet<TextBlock> TextBlock { get; set; }
        public IDbSet<Image> Image { get; set; }
        public IDbSet<ImageContent> ImageContent { get; set; }
        #endregion

        /// <summary>
        /// The constructor, we provide the connectionstring to be used to it's base class.
        /// </summary>
        public MyContext() : base("name=MyConnectionstring")
        {
            //Disable lazy loading by default!
            Configuration.LazyLoadingEnabled = false;

            Database.SetInitializer<BorloContext>(null);
        }

        //SOME OTHER CODE
}

我仍然在內存中看到很多對象,我希望它們與實體框架的延遲加載有關。

管理內存使用量

我已經設置了幾個層次的網站;

  1. 控制器 - 通常的東西
  2. 服務 - 使用控制器中使用的使用語句。 這些服務是一次性的,包含UnitOfWork。 UnitOfWork在服務的構造函數中初始化,並在服務本身被處理時處理。
  3. UnitOfWOrk - UnitOfWork類包含一個包含上下文的只讀私有變量,以及一組實例化類型為T的通用存儲庫的屬性。再次,UnitOfWork是一次性的,它在調用Dispose方法時處理上下文。
  4. Generic Repository匹配一個接口,將DbContext帶到它的構造函數中,並通過接口提供一組基本方法。

下面是如何使用它的示例。

PartialController

public class PartialController : BaseController
    {
        //private readonly IGenericService<Menu> _menuService;
        //private readonly UnitOfWork _unitOfWork = new UnitOfWork();
        //private readonly MenuService _menuService;

        public PartialController()
        {
            //_menuService = new GenericService<Menu>();
            //_menuService = new MenuService();
        }

        /// <summary>
        /// Renders the mainmenu based on the correct systemname.
        /// </summary>
        [ChildActionOnly]
        public ActionResult MainMenu()
        {
            var viewModel = new MenuModel { MenuItems = new List<MenuItem>() };

            try
            {
                Menu menu;
                using (var service = ServiceFactory.GetMenuService())
                {
                    menu= service.GetBySystemName("MainMenu");
                }

                //Get the menuItems collection from somewhere
                if (menu.MenuItems != null && menu.MenuItems.Any())
                {
                    viewModel.MenuItems = menu.MenuItems.ToList();
                    return View(viewModel);
                }
            }
            catch (Exception exception)
            {
                //TODO: Make nice function of this and decide throwing or logging.
                if (exception.GetType().IsAssignableFrom(typeof(KeyNotFoundException)))
                {
                    throw;
                }
                else
                {
                    //TODO: Exception handling and logging
                    //TODO: If exception then redirect to 500-error page.
                }

            }

            return View(viewModel);
        }
    }

服務工廠

public class ServiceFactory
    {
        public static IService<Menu> GetMenuService()
        {
            return new MenuService();
        }
}

MenuService

public class MenuService : BaseService, IService<Menu>
{
private readonly UnitOfWork _unitOfWork;
private bool _disposed;

public MenuService()
{
    if (_unitOfWork == null)
    {
        _unitOfWork = new UnitOfWork();
    }
}

/// <summary>
/// Retrieves the menu by the provided systemname.
/// </summary>
/// <param name="systemName">The systemname of the menu.</param>
/// <returns>The menu if found. Otherwise null</returns>
public Menu GetBySystemName(string systemName)
{
    var menu = new Menu();

    if (String.IsNullOrWhiteSpace(systemName)) throw new ArgumentNullException("systemName","Parameter is required.");

    if (Cache.HasItem(systemName))
    {
        menu = Cache.GetItem(systemName) as Menu;
    }
    else
    {
        var retrievedMenu = _unitOfWork.MenuRepository.GetSingle(m => m.SystemName.Equals(systemName), "MenuItems,MenuItems.Page");

        if (retrievedMenu == null) return menu;

        try
        {
            var exp = GenericRepository<CORE.Entities.MenuItem>.IsPublished();
            var menuItems = (exp != null) ?
                retrievedMenu.MenuItems.AsQueryable().Where(exp).Select(MenuTranslator.Translate).OrderBy(mi => mi.SortOrder).ToList() :
                retrievedMenu.MenuItems.Select(MenuTranslator.Translate).OrderBy(mi => mi.SortOrder).ToList();

            menu.MenuItems = menuItems;
        }
        catch (Exception)
        {
            //TODO: Logging
        }

        Cache.AddItem(systemName, menu, CachePriority.Default, CacheDuration.Short);
    }

    return menu;
}

public IEnumerable<Menu> Get()
{
    throw new NotImplementedException();
}

~MenuService()
{
    Dispose(false);
}

protected virtual void Dispose(bool disposing)
{
    if (!_disposed)
    {
        if (disposing)
        {
            _unitOfWork.Dispose();
        }
    }
    _disposed = true;
}

public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}

}

GenericRepository

public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class, IEntityObject

{/// ///使用的數據庫上下文。 ///內部MyContext上下文;

/// <summary>
/// The loaded set of entities.
/// </summary>
internal DbSet<TEntity> DbSet;

/// <summary>
/// The constructor taking the databasecontext.
/// </summary>
/// <param name="context">The databasecontext to use.</param>
public GenericRepository(MyContext context)
{
    //Apply the context
    Context = context;

    //Set the entity type for the current dbset.
    DbSet = context.Set<TEntity>();
}
public IQueryable<TEntity> AsQueryable(bool publishedItemsOnly = true)
{
    if (!publishedItemsOnly) return DbSet;
    try
    {
        return DbSet.Where(IsPublished());
    }
    catch (Exception)
    {
        //TODO: Logging
    }

    return DbSet;
}

/// <summary>
/// Gets a list of items matching the specified filter, order by and included properties.
/// </summary>
/// <param name="filter">The filter to apply.</param>
/// <param name="includeProperties">The properties to include to apply eager loading.</param>
/// <param name="publishedItemsOnly">True if only publish and active items should be included, otherwise false.</param>
/// <returns>A collection of entities matching the condition.</returns>
public virtual IQueryable<TEntity> Get(Expression<Func<TEntity, bool>> filter, string includeProperties, bool publishedItemsOnly)
{
    var query = AsQueryable(publishedItemsOnly);

    if (filter != null)
    {
        query = query.Where(filter);
    }


    if (String.IsNullOrWhiteSpace(includeProperties))
        return query;

    //Include all properties to the dbset to enable eager loading.
    query = includeProperties.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).Aggregate(query, (current, includeProperty) => current.Include(includeProperty));

    return query;
}

}

簡而言之。 在我的代碼/情況中可能會導致以下問題:在加載主頁時,使用了驚人的200 MB或更多? 我注意到的一個奇怪的事情是,在頁面加載之前,內存從111 MB跳到232 MB,如下例所示;

大內存IIS

使用dotMemory 編輯跟蹤結果

在此輸入圖像描述

編輯2我加載主頁后的結果下方。 主頁現在為空,並且在全局asax中,單個調用一個服務。 我讓頁面打開一段時間然后刷新,導致所有的峰值。 剖析1

下面是一個更詳細的結果,顯然很多字符串占用了大量的內存..? 剖析細節

編輯3 dotMemory的不同視圖 在此輸入圖像描述在此輸入圖像描述

所以,現在圖像更加清晰。 dotMemory顯示,您的應用程序只占用9Mb內存,我們可以在快照視圖中看到這一點。 內存流量視圖也證實了這一點。 從分析開始分配~73Mb,已經收集了~65Mb到Snapshot#1點。

那么在實時數據圖表上顯示的總內存使用情況怎么樣,抱歉我之前沒有意識到你的應用程序內存使用量大部分都是第0代堆。 (而且我也錯過了你的應用程序在這個屏幕上的快照區塊上只使用了〜8Mb)。

Gen 0堆大小顯示在第0代中分配的最大字節數; 不表示在第0代中分配的當前字節數 http://msdn.microsoft.com/en-us/library/x2tyfybc(v=vs.110).aspx

根據我的口味,Gen 0堆大小看起來異常大,但它是.net垃圾收集器的內部細節,它有權這樣做。

我冒昧地建議您的應用程序在具有大量RAM和/或大CPU緩存的計算機上運行。 但它也可以是ASP服務器實現的特殊方面。

結論 - 您的應用程序內存使用沒有問題:)至少在加載主頁時。

PS我建議觀看dotMemory視頻教程 ,以了解如何使用它

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM