我正在阅读有关如何实现 IDisposable 的 MSDN 文章,但我不确定文章中引用的托管资源和本机资源之间的区别。
我有一个类,在处理它时必须处理它的 2 个字段。我应该将它们视为托管资源(仅在处置 = true 时处置)还是本机资源?
我正在阅读有关如何实现 IDisposable 的 MSDN 文章,但我不确定文章中引用的托管资源和本机资源之间的区别。
我有一个类,在处理它时必须处理它的 2 个字段。我应该将它们视为托管资源(仅在处置 = true 时处置)还是本机资源?
为布赖恩的回答和您的评论/问题添加一点:
托管/非托管资源之间的区别在于垃圾收集器知道托管资源而不知道非托管资源。我知道答案不是很具体,但差异很大。
为了帮助划清界限,这里是 GC 如何运行和清理内存的简短版本(并且可能充满了小错误):
垃圾收集器知道所有托管对象,但是当垃圾收集运行时,它最初并不知道任何给定对象是否仍在使用中或是否可以被释放。它通过最初将所有对象标记为垃圾,然后从应用程序根遍历到所有引用的对象来确定它是否可以清理对象。每个与根有关系的对象(直接或间接的引用)都被标记为可访问的,不再被视为垃圾。在 GC 运行完每个可到达的对象后,它会清理其余的对象,因为它们不再使用。
在几乎所有使用 .NET 框架对象的情况下,您都可以确保对象是托管的(.NET 提供了几乎所有非托管资源的托管包装器,以确保它们被正确清理);其他与 Win32 API 挂钩的第三方组件(或执行此操作的组件)是可能引起关注的对象。
有一些 .NET 对象可以被认为有些不受管理。图形库的组件就是一个例子。
大多数“.NET 内存泄漏”并不是真正意义上的内存泄漏。通常,当您认为您已从使用中删除了一个对象但实际上该对象仍然对应用程序有一些引用时,它们会发生。一个常见的例子是添加事件处理程序(obj.SomeEvent += OnSomeEvent -or- AddHandler obj.SomeEvent, AddressOf OnSomeEvent)而不是删除它们。
这些“延迟引用”在技术上不是内存泄漏,因为您的应用程序在技术上仍在使用它们;但是,如果它们足够多,您的应用程序可能会遭受严重的性能影响,并可能显示资源问题的迹象(OutOfMemoryExceptions、无法获得窗口句柄等)。
我是一名中级 .NET 开发人员,不幸的是,我亲身了解了这些问题。我建议使用 ANTS Profiler 来帮助熟悉挥之不去的引用(有免费试用版),或者如果您想使用 WinDbg 和 SOS.DLL 进行更深入的研究来查看托管堆。如果您决定研究后者,我建议您阅读 Tess Ferrandez 的博客;她有很多关于有效使用 Windbg 的很棒的教程和建议
托管资源是另一种托管类型,它实现IDisposable
. 您需要调用您使用Dispose()
的任何其他IDisposable
类型。本机资源是托管世界之外的任何东西,例如本机 Windows 句柄等。
编辑:在评论中回答问题(评论太长)
不,那只是一种托管类型。未实现的正确构造的类型IDisposable
将由垃圾收集器处理,您无需执行任何其他操作。如果您的类型直接使用本机资源(例如通过调用 Win32 库),您必须IDisposable
在您的类型上实现并在Dispose
方法中处理资源。如果您的类型使用由另一种实现的类型封装的本机资源IDisposable
,则必须在您的类型的方法中调用Dispose()
此类型的实例。Dispose
简短的回答是您在 CLR 背后(到操作系统)获得的任何东西都可以称为“本机”。
所以现在 CantStayManaged 在出价之前有两件事需要清理。
现在可以通过两种方式触发对象清理。
在这两种情况下,都应该释放非托管资源,否则“泄漏!”,“崩溃!” 等所有表面。但是您应该只在 Dispose() 前一种情况下尝试清理托管资源。在后者/终结器的情况下 - CLR 可能已经完成并收集了您的一些成员,因此您不应该访问它们(CLR 不保证最终确定对象图的顺序。)因此您可以通过保护来避免问题带if (AmIBeingCalledFromDispose)
警卫检查的托管清理
高温高压