我有一个使用Entity Framework 6运行的小型MVC web应用程序。通过浏览我的dev上的主页(例如www.mywebsite.dev)启动应用程序。机器,应用程序池启动,页面按预期加载。
认为主页非常轻量级,只能从数据库中获取一些东西(2个菜单,2个带文本的段落,3个带有3-4个对象的集合),应用程序池是已经>刚加载主页一次后200 MB(!)..
使用this和this文章我已经设法弄清楚如何分析管理内存,我还删除了一些阻止处理上下文的静态属性。 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
}
我仍然在内存中看到很多对象,我希望它们与实体框架的延迟加载相关。
我已经设置了几个层次的网站;
下面是如何使用它的示例。
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,如下例所示;
修改 跟踪带有dotMemory的结果
编辑2 在我加载主页后的结果下方。主页现在为空,并且在全局asax中,单个调用一个服务。我打开页面一段时间然后刷新,导致所有的峰值。
下面是一个更详细的结果,显然很多字符串占用了大量的内存..?
编辑3 dotMemory的不同视图
答案 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,以了解如何使用它