Saturday, 15 February 2014

asp.net mvc - C# Entity Framework high memory usage, memory leak? -



asp.net mvc - C# Entity Framework high memory usage, memory leak? -

i've got little mvc webapplication running using entity framework 6. when starting application browwsing homepage (eg. www.mywebsite.dev) on dev. machine applicationpool get's started , page loaded expected.

despithe fact homepage pretty ligthweight , get's few things database (2 menu's, 2 paragraphs text, , collection 3-4 objects) application pool > 200 mb (!) after loading homepage once..

using this , this article i've managed figure out how profile manage memory, , removed few static properties blocking disposal of context. 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> /// constructor, provide connectionstring used it's base of operations class. /// </summary> public mycontext() : base("name=myconnectionstring") { //disable lazy loading default! configuration.lazyloadingenabled = false; database.setinitializer<borlocontext>(null); } //some other code }

i still see lot of objects in memory expect they're related entity framework's lazy loading.

i've setup website few layers;

controller - usual stuff service - trought using statement used in controllers. services disposable , contain unitofwork. unitofwork initialized in constructor of service , disposed when service itsself get's disposed. unitofwork - unitofwork class contains readonly private variable containing context, set of properties instantiating generic repository of type t. again, unitofwork disposable , disposes context when dispose method called. the generic repository matches interface, takes dbcontext trought it's constructor , offers basic set of methods trough interface.

below illustration of how 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 mainmenu based on right systemname. /// </summary> [childactiononly] public actionresult mainmenu() { var viewmodel = new menumodel { menuitems = new list<menuitem>() }; seek { menu menu; using (var service = servicefactory.getmenuservice()) { menu= service.getbysystemname("mainmenu"); } //get menuitems collection somewhere if (menu.menuitems != null && menu.menuitems.any()) { viewmodel.menuitems = menu.menuitems.tolist(); homecoming view(viewmodel); } } grab (exception exception) { //todo: create nice function of , decide throwing or logging. if (exception.gettype().isassignablefrom(typeof(keynotfoundexception))) { throw; } else { //todo: exception handling , logging //todo: if exception redirect 500-error page. } } homecoming view(viewmodel); } }

servicefactory

public class servicefactory { public static iservice<menu> getmenuservice() { homecoming 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 menu provided systemname. /// </summary> /// <param name="systemname">the systemname of 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 required."); if (cache.hasitem(systemname)) { menu = cache.getitem(systemname) menu; } else { var retrievedmenu = _unitofwork.menurepository.getsingle(m => m.systemname.equals(systemname), "menuitems,menuitems.page"); if (retrievedmenu == null) homecoming menu; seek { 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; } grab (exception) { //todo: logging } cache.additem(systemname, menu, cachepriority.default, cacheduration.short); } homecoming 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> tentity : class, ientityobject

{ /// /// database context used. /// internal mycontext context;

/// <summary> /// loaded set of entities. /// </summary> internal dbset<tentity> dbset; /// <summary> /// constructor taking databasecontext. /// </summary> /// <param name="context">the databasecontext use.</param> public genericrepository(mycontext context) { //apply context context = context; //set entity type current dbset. dbset = context.set<tentity>(); } public iqueryable<tentity> asqueryable(bool publisheditemsonly = true) { if (!publisheditemsonly) homecoming dbset; seek { homecoming dbset.where(ispublished()); } grab (exception) { //todo: logging } homecoming dbset; } /// <summary> /// gets list of items matching specified filter, order , included properties. /// </summary> /// <param name="filter">the filter apply.</param> /// <param name="includeproperties">the properties include apply eager loading.</param> /// <param name="publisheditemsonly">true if publish , active items should included, otherwise false.</param> /// <returns>a collection of entities matching 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)) homecoming query; //include properties dbset enable eager loading. query = includeproperties.split(new[] {','}, stringsplitoptions.removeemptyentries).aggregate(query, (current, includeproperty) => current.include(includeproperty)); homecoming query; }

}

to create long story short. in code / situation perchance cause problem when loading homepage amazing 200 mb or more used? 1 unusual thing i've noticed before page loaded memory jumps 111 mb 232 mb in illustration below;

edit result trace dotmemory

edit 2 below results after loaded homepage. homepage empty , in global asax single phone call 1 service made. i've kept page open while , refreshed, resulting in peaks.

below more detailed result, apperently lot of strings taking lot of memory..?

edit 3 different view dotmemory

so, image much more clearer now. dotmemory displays, app takes 9mb of memory, can see on snapshot view. confirmed memory traffic view. ~73mb allocated origin of profiling , ~65mb collected snapshot #1 point.

what total memory usage displayed on real time info chart, sorry did not realized before of app memory usage generation 0 heap. (and missed app uses ~8mb on snapshot tile on screen).

gen 0 heap size displays maximum bytes can allocated in generation 0; does not indicate 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 abnormally big taste, internal details of .net garbage collector, , has right that.

i have ventured suggest app running on computer big amount of ram and/or big cpu cache. can special aspects of asp server implementation.

conclusion - there no problem app memory usage :) @ to the lowest degree on loading homepage.

p.s. recommend watch dotmemory video tutorials, in order larn how utilize it

c# asp.net-mvc entity-framework memory memory-leaks

No comments:

Post a Comment