英文:
How to avoid memory leaks with Autofac?
问题
I'm trying to better understand IoC/DI, in particular the concept of scopes.
我正在尝试更好地理解IoC/DI,特别是作用域的概念。
I'm using a sample WPF app (repo here) from a Pluralsight course (great service, BTW) and modifying it slightly. Naturally, it worked fine before my changes.
我正在使用来自这里的示例WPF应用程序(Pluralsight课程中的repo2)并进行了轻微修改。自然,在我进行修改之前它运行正常。
Here's the original code:
以下是原始代码:
Private Sub App_Startup(Sender As App, e As StartupEventArgs) Handles Me.Startup
Dim oBootStrapper As BootStrapper
Dim oContainer As IContainer
Dim oMainView As MainView
oBootStrapper = New BootStrapper
oContainer = oBootStrapper.GetContainer
oMainView = oContainer.Resolve(Of MainView)
oMainView.Show()
End Sub
...and here's what I changed it to:
... 这是我对它进行的更改:
Private Sub App_Startup(Sender As App, e As StartupEventArgs) Handles Me.Startup
Dim oBootStrapper As BootStrapper
Dim oContainer As IContainer
Dim oMainView As MainView
oBootStrapper = New BootStrapper
oContainer = oBootStrapper.GetContainer
Using oScope As ILifetimeScope = oContainer.BeginLifetimeScope
oMainView = oScope.Resolve(Of MainView)
oMainView.Show()
End Using
End Sub
However, this results in an ObjectDisposedException
when I try to add a new Friend:
然而,当我尝试添加一个新的朋友时,会导致ObjectDisposedException
:
Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it (or one of its parent scopes) has already been disposed.
This all seems to contradict what I'm finding in the official documentation:
It is important to always resolve services from a lifetime scope and not the root container.
That guidance is what led me to introduce the scope in the first place. But if a scope disposes the objects necessary for the application to function, how can we hope to use it? Further, WHY should we use it?
I'm confused. Is my proposed usage incorrect?
--EDIT--
To clarify, I'm seeking a solution to the conundrum posed by the official guidance: if we adhere to that our application will fail. But if we don't we risk memory leaks (according to the docs).
What to do?
这似乎与我在官方文档中找到的内容相矛盾:
重要的是始终从生命周期范围中解析服务,而不是从根容器中。
这一指导原则是我首次引入作用域的原因。但如果作用域处置了应用程序运行所必需的对象,我们如何希望使用它呢?此外,我们为什么应该使用它呢?
我感到困惑。我的提出的用法是否不正确?
--编辑--
为了澄清,我正在寻找解决官方指导所提出的难题的方法:如果我们遵循官方指南,我们的应用程序将失败。但如果我们不这样做,我们会面临内存泄漏的风险(根据文档)。要怎么办?
英文:
I'm trying to better understand IoC/DI, in particular the concept of scopes.
I'm using a sample WPF app (repo here) from a Pluralsight course (great service, BTW) and modifying it slightly. Naturally, it worked fine before my changes.
Here's the original code:
Private Sub App_Startup(Sender As App, e As StartupEventArgs) Handles Me.Startup
Dim oBootStrapper As BootStrapper
Dim oContainer As IContainer
Dim oMainView As MainView
oBootStrapper = New BootStrapper
oContainer = oBootStrapper.GetContainer
oMainView = oContainer.Resolve(Of MainView)
oMainView.Show()
End Sub
...and here's what I changed it to:
Private Sub App_Startup(Sender As App, e As StartupEventArgs) Handles Me.Startup
Dim oBootStrapper As BootStrapper
Dim oContainer As IContainer
Dim oMainView As MainView
oBootStrapper = New BootStrapper
oContainer = oBootStrapper.GetContainer
Using oScope As ILifetimeScope = oContainer.BeginLifetimeScope
oMainView = oScope.Resolve(Of MainView)
oMainView.Show()
End Using
End Sub
However, this results in an ObjectDisposedException
when I try to add a new Friend:
> Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it (or one of its parent scopes) has already been disposed.
This all seems to contradict what I'm finding in the official documentation:
> It is important to always resolve services from a lifetime scope and not the root container.
That guidance is what led me to introduce the scope in the first place. But if a scope disposes the objects necessary for the application to function, how can we hope to use it? Further, WHY should we use it?
I'm confused. Is my proposed usage incorrect?
--EDIT--
To clarify, I'm seeking a solution to the conundrum posed by the official guidance: if we adhere to that our application will fail. But if we don't we risk memory leaks (according to the docs).
What to do?
答案1
得分: 2
官方指南旨在帮助您防止不经意的内存泄漏。由于 Autofac 持有对可释放对象的引用,以便执行释放操作,如果您直接从容器中解析了 1000 个 IDisposable 对象,那么在不释放整个容器的情况下,无法让垃圾回收器清理它们。
如果您没有考虑到这一点,而是在长时间运行的应用程序中偶尔与数据库进行工作,工作可能会周期性地从容器中解析可释放的数据库上下文,即使调用代码执行了释放操作,容器仍然会保留它:内存泄漏。
然而,结合了有关单例等内容的文档。单例,即使您从子范围中解析它们,仍然"生活"在容器的根位置。为什么?因为如果允许范围释放共享的单例,那将对需要该单例的其他所有组件都构成问题。
"从范围解析对象" 的指南旨在阻止人们在只使用容器时引入不经意的内存泄漏。
如果您要解析的对象不可释放,并且该对象的整个依赖链上都没有可释放的对象,那么从容器中获取它 可能 是安全的。
这里的更大教训是要认真处理您的生命周期范围以及如何处理释放操作。我不懂 WPF,但如果应用程序的主窗口需要解析并需要在应用程序的整个生命周期内存在,那似乎是单例的理想用法...它在生命周期范围结束时不会被释放,因此您不会看到异常。Autofac 还有方法,可以选择不执行释放操作,从而阻止它保持这些引用。
但很难说"总是这样做"和"永远不要那样做",因为,正如您所见,生命周期范围和释放操作是一个复杂的主题。重要的是深入了解"为什么",以及内存管理和释放操作就是为什么。正确设置已注册组件的生命周期同样重要,出于同样的原因。
英文:
The official guidance is there to help you prevent inadvertent memory leaks. Since Autofac holds references to disposable things so that it can do the disposing, if you resolve 1000 IDisposable items from the container directly, there's no way to let garbage collection clean them up without disposing the whole container.
This is particularly bad if you're not thinking about that and, say, have a long running app that does occasional work with maybe a database - the work might resolve a disposable database context from the container periodically and even if the calling code executes dispose, the container will hang onto it: memory leak.
However, put that together with the docs about things like singletons. Singletons, even if you resolve them from a child scope, live at the container root. Why? Because if you let a scope dispose a shared singleton it would be a problem for everything else needing the singleton.
The "resolve things from a scope" guidance is there to stop folks from shooting themselves in the foot with memory leaks they don't realize they're introducing by only using the container.
If the thing you're resolving is not disposable, AND nothing in the entire dependency chain for that thing is disposable, you're probably safe getting it from the container.
The larger lesson here is be diligent with your lifetime scopes and how you want disposal handled. I'm not a WPF guy, but if the main window for the app needs to be resolved and needs to live for the entire duration of the app, it seems like that's a perfect use for a singleton... which wouldn't have been disposed at the end of that lifetime scope and you wouldn't have seen the exception. Autofac also has ways you can opt out of making it do disposal, which stops it from holding those references.
But it's super hard to say "always do this" and "never do that" because, as you see, lifetime scope and disposal is a complex topic. It's important to dig into why, and memory management and disposal is why. Getting the lifetimes right on your registered components is equally important for the same reasons.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论