C # Entity Framework high memory, memory leak?

I have a small MVC web application running using Entity Framework 6. When I launch the application, navigating to the home page (eg www.mywebsite.dev) on my dev. the machine starts up and the page loads as expected.

Cast aside the fact that the home page is quite weighty and only get a few things from the database (2 menus, 2 paragraphs with text and a collection with 3-4 objects), the application pool is already> 200 MB (!) After loading the home page immediately page ..

Using this and this article I've 'managed to figure out how to profile control memory, and I've also removed a few static properties that block context deletion. DbContext is disabled from lazy loading,

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 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 them to be related to entity lazy loading.

Manage Memory usage

I have set up a site with multiple layers;

  • Controller - Normal Material
  • Service. The using statement used in controllers is used. The services are one-off and contain UnitOfWork. UnitOfWork is initialized in the constructor of the service and placed when the service itself is disposed of.
  • UnitOfWOrk - The UnitOfWork class contains only a readonly variable containing a context, along with a set of properties that instantiate a Generic Repository of type T. Again, UnitOfWork is a one-off and provides a context when the Dispose method is called.
  • The shared repository is the same as the interface, it calls the DbContext constructor and offers a basic set of methods through the interface.

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

      

}

GenericRepository

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

      

{/// /// The database context is used. /// internal 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;
}

      

}

Shortly speaking. What in my code / situation can cause the problem when more than 200MB or more is used when loading only the home page? One strange thing I noticed is that memory jumps from 111 MB to 232 MB before loading the page in the example below;

Large memory IIS

EDIT Result from tracing with dotMemory

enter image description here

EDIT 2 Below are the results after I loaded the homepage. The home page is now empty and one call to one service is made in the global asax. I kept the page open for a while and then refreshed, resulting in all the spikes. Profiling 1

Below is a more detailed result, with a very large number of strings taking up a lot of memory. Profiling detail

EDIT 3 Different view from dotMemory enter image description hereenter image description here

+3


source to share


1 answer


So, the image has become much clearer. dotMemory displays that your application is only using 9MB of memory, we can see this in the Snapshot view. This is also confirmed by looking at the memory traffic. ~ 73Mb has been allocated since the start of profiling, and ~ 65Mb has already been collected to snapshot # 1.

How about the total memory usage displayed in the realtime data chart, sorry I didn't understand earlier, then most of your application memory usage is the generation 0 heap (And also I missed that your application only uses ~ 8MB in the snapshot on this screen).

Gen 0 heap size represents 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 heap size looks unusually large for my taste, but these are internal details of the .net garbage collector and it is entitled to it.



I ventured to assume that your application is running on a computer with a lot of RAM and / or a large processor cache. But these can also be special aspects of the ASP server implementation.

Conclusion - no problem using your application memory. At least when loading only the home page.

PS I would recommend watching the dotMemory video tutorials to learn how to use it.

+4


source







All Articles