简体   繁体   English

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

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

I've got a small MVC webapplication running using Entity Framework 6. When starting the application by browwsing to the homepage (eg. www.mywebsite.dev) on my dev. 我有一个使用Entity Framework 6运行的小型MVC web应用程序。通过浏览我的dev上的主页(例如www.mywebsite.dev)启动应用程序。 machine the applicationpool get's started and the page get loaded as expected. 机器应用程序池启动并按预期加载页面。

Despithe the fact that the homepage is pretty ligthweight and only get's a few things from the database (2 menu's, 2 paragraphs with text, and a collection with 3-4 objects) the application pool is already > 200 MB (!) after just loading the homepage once.. 鄙视这样一个事实,即主页非常轻量级,只能从数据库中获取一些东西(2个菜单,2个带文本的段落,以及带有3-4个对象的集合),应用程序池在加载后已经超过200 MB(!)主页一次..

Using this and this article i've managed to figure out how to profile the manage memory, and I also removed a few static properties blocking the disposal of the context. 使用这个这个文章中,我已经设法弄清楚如何配置文件管理存储,而且我也删除了几个静态属性阻断支配的上下文。 The DbContext has lazy loading disabled, 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
}

I still see a lot of objects in memory from which I expect they're related to entity framework's lazy loading. 我仍然在内存中看到很多对象,我希望它们与实体框架的延迟加载有关。

管理内存使用量

I've setup the website with a few layers; 我已经设置了几个层次的网站;

  1. Controller - The usual stuff 控制器 - 通常的东西
  2. Service - Trought a using statement used in the controllers. 服务 - 使用控制器中使用的使用语句。 The services are disposable and contain a UnitOfWork. 这些服务是一次性的,包含UnitOfWork。 The UnitOfWork is initialized in the Constructor of the service and disposed when the service itsself get's disposed. UnitOfWork在服务的构造函数中初始化,并在服务本身被处理时处理。
  3. UnitOfWOrk - The UnitOfWork class contains a readonly private variable containing the context, together with a set of properties instantiating a Generic Repository of type T. Again, the UnitOfWork is disposable and it disposes the context when the Dispose method is called. UnitOfWOrk - UnitOfWork类包含一个包含上下文的只读私有变量,以及一组实例化类型为T的通用存储库的属性。再次,UnitOfWork是一次性的,它在调用Dispose方法时处理上下文。
  4. The Generic Repository matches an interface, takes the DbContext trought it's constructor and offers a basic set of methods trough an interface. Generic Repository匹配一个接口,将DbContext带到它的构造函数中,并通过接口提供一组基本方法。

Below an example of how this is used. 下面是如何使用它的示例。

PartialController 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 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 GenericRepository

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

{ /// /// The database context used. {/// ///使用的数据库上下文。 /// internal MyContext Context; ///内部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;
}

} }

To make a very long story short. 简而言之。 What in my code / situation could possibly cause the problem that when loading just the homepage a amazing 200 MB or more is used? 在我的代码/情况中可能会导致以下问题:在加载主页时,使用了惊人的200 MB或更多? One strange thing i've noticed is that just before the page is loaded the memory jumps from 111 mb to 232 MB in the example below; 我注意到的一个奇怪的事情是,在页面加载之前,内存从111 MB跳到232 MB,如下例所示;

大内存IIS

EDIT Result from trace with dotMemory 使用dotMemory 编辑跟踪结果

在此输入图像描述

EDIT 2 Below the results after i loaded the homepage. 编辑2我加载主页后的结果下方。 The homepage is now empty and in the global asax a single call to one service is made. 主页现在为空,并且在全局asax中,单个调用一个服务。 I've kept the page open for a while and then refreshed, resulting in all the peaks. 我让页面打开一段时间然后刷新,导致所有的峰值。 剖析1

Below a more detailed result, with apperently a lot of strings taking a lot of memory..? 下面是一个更详细的结果,显然很多字符串占用了大量的内存..? 剖析细节

EDIT 3 Different view from dotMemory 编辑3 dotMemory的不同视图 在此输入图像描述在此输入图像描述

So, image is much more clearer now. 所以,现在图像更加清晰。 dotMemory displays, that your app takes only 9Mb of memory, we can see this on the Snapshot view. dotMemory显示,您的应用程序只占用9Mb内存,我们可以在快照视图中看到这一点。 This is also confirmed by Memory Traffic view. 内存流量视图也证实了这一点。 ~73Mb was allocated from the beginning of profiling and ~65Mb was already collected to the Snapshot #1 point. 从分析开始分配~73Mb,已经收集了~65Mb到Snapshot#1点。

What about total memory usage displayed on the real time data chart, sorry I did not realized earlier then the most of your app memory usage is generation 0 heap. 那么在实时数据图表上显示的总内存使用情况怎么样,抱歉我之前没有意识到你的应用程序内存使用量大部分都是第0代堆。 (And also I missed that your app uses only ~8Mb on the snapshot tile on this screen). (而且我也错过了你的应用程序在这个屏幕上的快照区块上只使用了〜8Mb)。

Gen 0 heap size displays the maximum bytes that can be allocated in generation 0; Gen 0堆大小显示在第0代中分配的最大字节数; it does not indicate the current number of bytes allocated in generation 0 . 不表示在第0代中分配的当前字节数 http://msdn.microsoft.com/en-us/library/x2tyfybc(v=vs.110).aspx http://msdn.microsoft.com/en-us/library/x2tyfybc(v=vs.110).aspx

Gen 0 heap size looks abnormally big for my taste, but it is an internal details of .net garbage collector, and it has a right to do that. 根据我的口味,Gen 0堆大小看起来异常大,但它是.net垃圾收集器的内部细节,它有权这样做。

I have ventured to suggest that your app is running on the computer with big amount of RAM and/or with big CPU cache. 我冒昧地建议您的应用程序在具有大量RAM和/或大CPU缓存的计算机上运行。 But it can be also special aspects of ASP server implementation. 但它也可以是ASP服务器实现的特殊方面。

Conclusion - there is no problem with your app memory usage :) At least on loading just the homepage. 结论 - 您的应用程序内存使用没有问题:)至少在加载主页时。

PS I would recommend to watch dotMemory video tutorials , in order to learn how to use it PS我建议观看dotMemory视频教程 ,以了解如何使用它

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM