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

时间:2014-11-13 11:50:26

标签: c# asp.net-mvc entity-framework memory memory-leaks

我有一个使用Entity Framework 6运行的小型MVC web应用程序。通过浏览我的dev上的主页(例如www.mywebsite.dev)启动应用程序。机器,应用程序池启动,页面按预期加载。

认为主页非常轻量级,只能从数据库中获取一些东西(2个菜单,2个带文本的段落,3个带有3-4个对象的集合),应用程序池是已经>刚加载主页一次后200 MB(!)..

使用thisthis文章我已经设法弄清楚如何分析管理内存,我还删除了一些阻止处理上下文的静态属性。 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
}

我仍然在内存中看到很多对象,我希望它们与实体框架的延迟加载相关。

Manage Memory usage

我已经设置了几个层次的网站;

  1. 控制器 - 通常的东西
  2. 服务 - 使用控制器中使用的using语句。这些服务是一次性的,包含UnitOfWork。 UnitOfWork在服务的构造函数中初始化,并在服务本身被处置时处理。
  3. UnitOfWOrk - UnitOfWork类包含一个包含上下文的只读私有变量,以及一组实例化类型为T的通用存储库的属性。再次,UnitOfWork是一次性的,它在调用Dispose方法时处理上下文。 / LI>
  4. Generic Repository匹配一个接口,将DbContext带到它的构造函数中,并通过一个接口提供一组基本的方法。
  5. 下面是如何使用它的示例。

    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);
            }
        }
    

    的ServiceFactory

    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,如下例所示;

    Large memory IIS

    修改 跟踪带有dotMemory的结果

    enter image description here

    编辑2 在我加载主页后的结果下方。主页现在为空,并且在全局asax中,单个调用一个服务。我打开页面一段时间然后刷新,导致所有的峰值。 Profiling 1

    下面是一个更详细的结果,显然很多字符串占用了大量的内存..? Profiling detail

    编辑3 dotMemory的不同视图 enter image description here enter image description here

1 个答案:

答案 0 :(得分:4)

因此,图像现在更加清晰。 dotMemory显示,您的应用程序只占用9Mb内存,我们可以在快照视图中看到这一点。内存流量视图也证实了这一点。从分析开始就分配了~73Mb,并且已经收集了~65Mb到快照#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服务器实现的特殊方面。

结论 - 您的应用内存使用没有问题:)至少在加载主页时。

P.S。我建议您观看dotMemory video tutorials,以了解如何使用它