避免Castle Windsor引起的内存泄露
目录
CASTLE WINDSOR: AVOID MEMORY LEAKS BY LEARNING THE UNDERLYING MECHANICS
Lifestyles
Service Locator
Release Policy
Lifecycle concerns
But why and exactly when does the memory leak happen?
Why does Windsor track components with a decommission concern ?
Conclusion
原文地址:
http://nexussharp.wordpress.com/2012/04/21/castle-windsor-avoid-memory-leaks-by-learning-the-underlying-mechanics/
CASTLE WINDSOR: AVOID MEMORY LEAKS BY LEARNING THE UNDERLYING MECHANICS
Lifestyles
In am not going to explain all the different lifestyles Windsor has as you could read up on them on the Castle Wiki but for the sake of completeness I will list the most important ones and explain them in my own words
- Singleton: This is actually the default lifestyle, means there will be only 1 instance of that class in your container (think static)
- PerThread: There will only 1 instance per thread (think threadstatic)
- PerWebRequest: There will only be 1 instance per web request
- Pooled: There will be multiple instances of the same object but in a pool with a minimum pool size and a maximum pool size
- Transient: Each time an instance is requested windsor will initialize a new one
Service Locator
Part of ASP.NET MVC 3 is the IDependencyResolver interface which is basically the contract of the service locator pattern (described by Martin Fowler here) or better said anti pattern!
If you are using Castle Windsor combined with service location you can get a lot of memory issues basically because the contract has no method for releasing your services/components.
When you are using Windsor you should always try to avoid getting an instance from the container yourself and when you have to, remember to always release the component after using it !
1 2 3 | IComponent component = container.Resolve(); component.Act(); container.Release(component); |
But why is that? Why do we need to release our components that we ask for explicitly?
Release Policy
Again this is explained very detailed on the Castle Wiki but i will list the important ones.
- LifecycledComponentsReleasePolicy (default)
- NoTrackingReleasePolicy
By default Windsor will use
LifecycledComponentsReleasePolicy
which keeps track of all components that were created, and upon releasing them, invokes all their decommission lifecycle steps.
In other words your garbage collector will not be able to cleanup if your container still tracks your component. Which will result into memory leaks. Now i’ve seen many posts and questions on the web about this where people are advising to use the NoTrackingReleasePolicy, don’t because the default release policy is actually a good thing!
Lifecycle concerns
See Castle Wiki for a detailed description.
- Creation – commission concerns : everything happening within
container.Resolve
or similar method - destruction – decommission concerns : everything happening within and/or after
container.ReleaseComponent
.
A good example of a decommission concern is when your component implements IDisposable, the container will automatically recognize this as a decommission concern.
But why and exactly when does the memory leak happen?
If you look back on the lifestyles you will see that there are lifestyles, where the begin and end of the lifetime of the component is clear:
- Singleton : the life of the component start at commission (resolve) and ends when the container is disposed
- PerThread : the life of the component ends when the thread ends
- PerWebRequest : …
But what about Transient and Pooled ? Especially with these you gotta watch out !
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | public interface IComponent { } public class MyComponent: IComponent { } public interface IDisposableComponent: IComponent,IDisposable { bool IsDisposed { get ; } } public class MyDisposableComponent: IDisposableComponent { public bool IsDisposed { get ; private set ; } public void Dispose() { IsDisposed = true ; } } public interface IService { IDisposableComponent Component { get ; } } public class MyServiceUsingComponent: IService { public IDisposableComponent Component { get ; private set ; } public MyServiceUsingComponent(IDisposableComponent component) { Component = component; } } |
What happens if we resolve a transient
1 2 3 4 5 | container.Register(Component.For<IComponent>() .ImplementedBy<MyComponent>() .LifestyleTransient()); var component=container.Resolve<IComponent>(); Assert.IsFalse(container.Kernel.ReleasePolicy.HasTrack(component)); |
So this actually means that Windsor is not tracking the component, which means the garbage collector will be able to clean up this instance.
What happens if we resolve a transient with a decommission concern for instance IDisposable
1 2 3 4 5 | container.Register(Component.For<IDisposableComponent>() .ImplementedBy<MyDisposableComponent>() .LifeStyle.Transient); var component = container.Resolve<IDisposableComponent>(); Assert.IsTrue(container.Kernel.ReleasePolicy.HasTrack(component)); |
So if we don’t release the component after using it, the garbage collector will not be able to pick it up as Windsor is still referencing it and your dispose method will never get invoked !
What happens if we resolve a transient component that is dependent on another transient component with a decommission concern ?
1 2 3 4 5 6 7 8 | container.Register(Component.For<IDisposableComponent>() .ImplementedBy<MyDisposableComponent>() .LifeStyle.Transient); container.Register(Component.For<IService>() .ImplementedBy<MyServiceUsingComponent>() .LifeStyle.Transient); var service = container.Resolve<IService>(); Assert.IsTrue(container.Kernel.ReleasePolicy.HasTrack(service)); |
The tracking will propagate onto the parent service so again if we don’t release the service after using it, we will get a memory leak !
Always release after you are done
1 2 3 4 5 6 | Container.Register(Component.For<IDisposableComponent>() .ImplementedBy<MyDisposableComponent>() .LifeStyle.Transient); var component = Container.Resolve<IDisposableComponent>(); Container.Release(component); Assert.IsTrue(component.IsDisposed); |
Why does Windsor track components with a decommission concern ?
At the end of the lifetime of the component, either trough a implicit (Component or Component dependent on ends life) or explicit release (trough a container.Release), Windsor will execute all decommission steps. For instance when your component implements IDisposable, Windsor will call the Dispose method.
Conclusion
The ‘LifecycledComponentsReleasePolicy’ is great because it will track your components that have no real end of life defined and will cleanup after you. But especially be aware if you have a singleton component taking in a transient dependency with a decommission concern, because even if you release your singleton component after using it, it will not release the transient dependency immediatly, it will have to wait until your singleton’s life ends!
Because the real releasing (think disposing) of your transient dependency will happen at the end the life of the singleton component, most of the time when your application stops, only then will the container release the reference to the transient dependency and eventually call the dispose method. And even then this is just 1 instance of that transient component, so that will not cause a memory issue.
But in a real world scenario where you follow the advice of if you have to resolve yourself, you release after using the component. You will have no memory leaks !!!
So bye bye Service Locator without a release method and hello really powerfull IOC !!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)