C#实体框架内存使用率高,内存泄漏? [英] C# Entity Framework high memory usage, memory leak?

查看:85
本文介绍了C#实体框架内存使用率高,内存泄漏?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用Entity Framework 6运行的小型MVC Web应用程序.通过浏览开发人员的主页(例如,www.mywebsite.dev)启动应用程序时.对applicationpool进行启动,并按预期加载页面.

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. machine the applicationpool get's started and the page get loaded as expected.

鄙视这个事实,即主页相当轻巧,只能从数据库中获取一些东西(2个菜单,2个带有文本的段落以及一个3-4个对象的集合),应用程序池已经> 200 MB(!)只需加载一次首页.

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..

使用这篇文章我设法弄清楚了如何配置管理内存,并且我还删除了一些静态属性来阻止上下文的处理. . DbContext已禁用延迟加载,

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,

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. 控制器-常用的东西
  2. Service-在控制器中使用了using语句.这些服务是一次性的,包含一个UnitOfWork.在服务的构造函数中初始化UnitOfWork,并在服务本身被处置时将其处置.
  3. UnitOfWOrk-UnitOfWork类包含一个只读私有变量,该私有变量包含上下文以及一组实例化类型T的通用存储库的属性.再次,UnitOfWork是可抛弃的,当调用Dispose方法时,它将处置上下文. /li>
  4. 通用存储库与接口匹配,将DbContext用作其构造函数,并通过接口提供基本的方法集.
  1. Controller - The usual stuff
  2. Service - Trought a using statement used in the controllers. The services are disposable and contain a UnitOfWork. The UnitOfWork is initialized in the Constructor of the service and disposed when the service itsself get's disposed.
  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.
  4. The Generic Repository matches an interface, takes the DbContext trought it's constructor and offers a basic set of methods trough an interface.

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

Below an example of how this is used.

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

}

通用存储库

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

{ /// ///使用的数据库上下文. /// 内部MyContext上下文;

{ /// /// The database context used. /// internal MyContext Context;

/// <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;

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? 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;

编辑 用dotMemory跟踪的结果

EDIT Result from trace with dotMemory

编辑2 我加载主页后的结果下方.主页现在为空,并且在全局asax中仅对一个服务进行了一次调用.我将页面打开了一段时间,然后刷新了所有峰值.

EDIT 2 Below the results after i loaded the homepage. The homepage is now empty and in the global asax a single call to one service is made. I've kept the page open for a while and then refreshed, resulting in all the peaks.

下面是更详细的结果,许多字符串显然占用大量内存.

Below a more detailed result, with apperently a lot of strings taking a lot of memory..?

编辑3 与dotMemory不同的观点

EDIT 3 Different view from dotMemory

推荐答案

因此,图像现在更加清晰了. dotMemory显示,您的应用仅占用9Mb的内存,我们可以在Snapshot视图中看到这一点.内存流量视图也确认了这一点.从概要分析开始就分配了约73Mb的内存,并且已将快照快照#1的位置收集了约65Mb.

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. 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.

关于实时数据图表上显示的总内存使用情况如何,对不起,我之前没有意识到,那么您的大多数应用程序内存使用情况都是0代堆. (而且我也错过了您的应用程序在此屏幕上的快照图块上仅使用〜8Mb).

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. (And also I missed that your app uses only ~8Mb on the snapshot tile on this screen).

Gen 0堆大小显示了可以分配的最大字节数 第0代;它不表示当前字节数 在第0代中分配. http://msdn.microsoft.com/zh-我们/library/x2tyfybc(v=vs.110).aspx

Gen 0 heap size displays the maximum bytes that can be allocated in generation 0; it does not indicate the current number of bytes allocated in generation 0. http://msdn.microsoft.com/en-us/library/x2tyfybc(v=vs.110).aspx

Gen 0堆大小对于我的口味来说看起来异常大,但这是.net垃圾收集器的内部细节,它有权这样做.

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.

我冒昧地建议您的应用程序在具有大量RAM和/或具有大量CPU缓存的计算机上运行.但这也可能是ASP服务器实现的特殊方面.

I have ventured to suggest that your app is running on the computer with big amount of RAM and/or with big CPU cache. But it can be also special aspects of ASP server implementation.

结论-您的应用程序内存使用没有问题:)至少仅加载首页上即可.

Conclusion - there is no problem with your app memory usage :) At least on loading just the homepage.

P.S.我建议您观看 dotMemory视频教程.使用方法

P.S. I would recommend to watch dotMemory video tutorials, in order to learn how to use it

这篇关于C#实体框架内存使用率高,内存泄漏?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆