Memory leak - w3wp.exe memory goes up to 99% of server memory in couple of hours. If I remove *AddControllerLoggingFunctionality(), *memory is constant at very low level.
If i override dispose method of some controller, I can see that it is called (in this second case without proxy) when this method is executed: *public override void ReleaseController(IController controller)* * {* * container.Kernel.ReleaseComponent(controller);* * }* If I have AddControllerLoggingFunctionality, then dispose method is never called. So, I'm 99% sure that this method somehow prevent castle to dispose controllers from memory. CreateInterfaceProxyWithTarget always return type : Castle.Proxies.IControllerProxy (System.Web.Mvc.IController{Castle.Proxies.IControllerProxy}) But inside this type, there is _target variable, which has always different type: SM.UI.Web.Controllers.DemoController, SM.UI.Web.Controllers.HomeController,.. It can been seen from original debugger picture I have attached how returned controller looks like. I have commented out the options and now function looks like: *public static TInterface AddControllerLoggingFunctionality<TInterface>(this TInterface implementation) where TInterface : class{* * if (implementation == null)* * {* * throw new ArgumentNullException("implementation");* * }* * if (!typeof(TInterface).IsInterface)* * {* * throw new Exception("Type of 'TInterface' must be interface.");* * }* * return (TInterface)proxyGenerator.CreateInterfaceProxyWithTarget<TInterface>(* * implementation, new ControllerLoggingInterceptor(implementation.GetType())* * );* * }* but result is still the same, dispose of controller is never called. The version is: Castle.Windsor 3.0.0 for .NETFramework v4.0 Castle.Core 3.3.3 for .NETFramework v4.5 Thanks for help. On Monday, 20 February 2017 16:13:25 UTC+1, Jonathon Rossi wrote: > > Apologies for not coming back to your StackOverflow question ( > http://stackoverflow.com/questions/42272082/castle-windsor-proxy-generate-memory-leak) > > after you added the code for "AddControllerLoggingFunctionality". > > Can you further explain what you mean by a memory leak, i.e. what exactly > is leaking? The first thing I'd check is what proxy type is being returned > by CreateInterfaceProxyWithTarget, i.e. is it the same type each time or is > DynamicProxy generating a new type each time and therefore bloating its > unclearable type cache. Since you are using ProxyGenerationOptions > especially with AdditionalAttributes this is an area we've had a defect and > some changes recently ( > https://github.com/castleproject/Core/blob/master/CHANGELOG.md), do you > still get the memory leak if you comment out the line with > "options.AdditionalAttributes.Add(...)". What version are you using? > > https://github.com/castleproject/Core/issues/77 > https://github.com/castleproject/Core/pull/78 (4.0.0-alpha001) > https://github.com/castleproject/Core/pull/219 (4.0.0-beta002) > > P.S. you should use the Stopwatch class which uses the very quick high > precision query performance timer rather than DateTime.Now. > > On Tue, Feb 21, 2017 at 12:23 AM erazem <zupa...@gmail.com <javascript:>> > wrote: > >> In my MVC application there is Castle Windsor used as it is described >> here: >> >> https://github.com/castleproject/Windsor/blob/master/docs/mvc-tutorial-part-2-plugging-windsor-in.md >> >> When "container.Kernel.ReleaseComponent(controller)" is called, this >> controller is not disposed and remains in memory forever - it introduce >> memory leak. >> The reason for this must be: >> >> There is only one difference, when you get a controller instance, there >> is called extension method, which adds some login functionality to >> controller: >> >> *protected override IController >> GetControllerInstance(RequestContext requestContext, Type controllerType)* >> * {* >> * if (controllerType == null)* >> * {* >> * throw new HttpException(404,* >> * $"The controller for path >> '{requestContext.HttpContext.Request.Path}' could not be found.");* >> >> >> * }* >> >> * return >> ((IController)container.Kernel.Resolve(controllerType)).AddControllerLoggingFunctionality(); >> >> // if it is without extension methos, there is no memory leak:* >> >> * //return >> (IController)container.Kernel.Resolve(controllerType);* >> * }* >> >> The controller instance looks like this: >> >> >> <https://lh3.googleusercontent.com/-caNhjJ3C8Uc/WKr7UycBBaI/AAAAAAAAC6Q/T-pMPEje3NkSlz29whUja5nQeYZklYOiwCLcB/s1600/controller.JPG> >> >> So, AddControllerLoggingFunctionality is responsible for memory leak. >> This method is inside Logger class and use Castle DynamicProxy >> interceptor: >> >> *public static class Logger* >> * {* >> * private static readonly Castle.DynamicProxy.ProxyGenerator >> proxyGenerator;* >> >> * static Logger()* >> * {* >> * proxyGenerator = new Castle.DynamicProxy.ProxyGenerator();* >> * >> Castle.DynamicProxy.Generators.AttributesToAvoidReplicating.Add(typeof(ServiceContractAttribute));* >> * }* >> >> * public static TInterface >> AddControllerLoggingFunctionality<TInterface>(this TInterface >> implementation) where TInterface : class* >> * {* >> * if (implementation == null)* >> * {* >> * throw new ArgumentNullException("implementation");* >> * }* >> >> * if (!typeof(TInterface).IsInterface)* >> * {* >> * throw new Exception("Type of 'TInterface' must be >> interface.");* >> * }* >> >> * Castle.DynamicProxy.ProxyGenerationOptions options = new >> Castle.DynamicProxy.ProxyGenerationOptions();* >> >> * var origAttribs = >> implementation.GetType().GetCustomAttributesData();* >> * if (origAttribs != null)* >> * {* >> * foreach (var origAttrib in origAttribs)* >> * {* >> * >> options.AdditionalAttributes.Add(AttributeUtil.CreateBuilder(origAttrib));* >> * }* >> * }* >> >> * return >> (TInterface)proxyGenerator.CreateInterfaceProxyWithTarget<TInterface>(* >> * implementation,* >> * options,* >> * new >> ControllerLoggingInterceptor(implementation.GetType()));* >> * }* >> >> * private class ControllerLoggingInterceptor : >> Castle.DynamicProxy.IInterceptor* >> * {* >> * private readonly Type typeTarget = null;* >> * private readonly ILog logger = null;* >> >> * public ControllerLoggingInterceptor(Type type)* >> * {* >> * this.typeTarget = type;* >> * this.logger = LogManager.GetLogger(this.typeTarget);* >> * }* >> >> * public ControllerLoggingInterceptor()* >> * {* >> * this.logger = >> LogManager.GetLogger("NullTargetAutoLogger");* >> * }* >> >> * public void Intercept(Castle.DynamicProxy.IInvocation >> invocation)* >> * {* >> * bool success = false;* >> * string sbTraceMethod = null;* >> * long ticksBefore = 0;* >> * long ticksAfter = 0;* >> >> * try* >> * {* >> * if ((this.logger != null) && >> this.logger.Logger.IsEnabledFor(log4net.Core.Level.Trace))* >> * {* >> * sbTraceMethod = >> GetControllerActionName(invocation);* >> * logger.TraceFormat("Entering {0}", >> sbTraceMethod);* >> * }* >> >> * ticksBefore = DateTime.Now.Ticks;* >> * invocation.Proceed();* >> * ticksAfter = DateTime.Now.Ticks;* >> * success = true;* >> * }* >> * catch (Exception)* >> * {* >> * ticksAfter = DateTime.Now.Ticks;* >> * success = false;* >> * throw;* >> * }* >> * finally* >> * {* >> * if (this.logger != null* >> * && >> this.logger.Logger.IsEnabledFor(log4net.Core.Level.Trace))* >> * {* >> * logger.TraceFormat("Leaving {0} with success: >> {1} elapsed time: {2}ms", sbTraceMethod ?? "[no info]", success, >> TimeSpan.FromTicks(ticksAfter - ticksBefore).TotalMilliseconds);* >> * }* >> * }* >> * }* >> >> * private static string >> GetControllerActionName(Castle.DynamicProxy.IInvocation invocation)* >> * {* >> * ParameterInfo[] methodParams = >> invocation.Method.GetParameters();* >> * ParameterInfo activityParameterInfo = >> methodParams.FirstOrDefault(x =>* >> * >> x.ParameterType.Equals(typeof(System.Web.Routing.RequestContext)));* >> >> * if (invocation != null && invocation.Arguments != null)* >> * {* >> * System.Web.Routing.RequestContext rq = >> invocation.Arguments[activityParameterInfo.Position] as >> System.Web.Routing.RequestContext;* >> >> * if (rq != null && rq.RouteData != null && >> rq.RouteData.Values.Count >= 2)* >> * {* >> * return String.Format(* >> * "{0}.{1}()",* >> * rq.RouteData.Values["controller"],* >> * rq.RouteData.Values["action"]);* >> * }* >> * }* >> * return "UNKNOWN";* >> * }* >> * }* >> *}* >> The controller object is like this: >> >> What should I change to avoid memory leak and keep functionality as it is? >> >> Thanks >> >> -- >> You received this message because you are subscribed to the Google Groups >> "Castle Project Users" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to castle-project-users+unsubscr...@googlegroups.com <javascript:>. >> To post to this group, send email to castle-pro...@googlegroups.com >> <javascript:>. >> Visit this group at https://groups.google.com/group/castle-project-users. >> For more options, visit https://groups.google.com/d/optout. >> > -- You received this message because you are subscribed to the Google Groups "Castle Project Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to castle-project-users+unsubscr...@googlegroups.com. To post to this group, send email to castle-project-users@googlegroups.com. Visit this group at https://groups.google.com/group/castle-project-users. For more options, visit https://groups.google.com/d/optout.