【问题标题】:What is the difference between managed and native resources when disposing? (.NET)处置时托管资源和本机资源有什么区别? (。网)
【发布时间】:2009-01-29 19:11:02
【问题描述】:

我正在阅读MSDN article about how to implement IDisposable,但我不确定文章中引用的托管资源和本地资源之间的区别。

我有一个类,在处理它时必须处理它的 2 个字段。我应该将它们视为托管资源(仅在 disposing = true 时释放)还是原生资源?

【问题讨论】:

    标签: .net resources native idisposable


    【解决方案1】:

    为 Brian 的回答和您的评论/问题添加一点内容:

    托管/非托管资源之间的区别在于垃圾收集器知道托管资源而不知道非托管资源。我知道答案不是很具体,但差别很大。

    为了帮助划清界限,这里简要介绍一下 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 的很棒的教程和建议

    【讨论】:

    • 我喜欢你的回答。托管资源和非托管资源之间最重要的区别不是它们是否存在于 GC 世界中,而是如果它们被丢弃,GC 是否知道如何进行所有必要的清理。如果清理失败可能会损害系统功能,但二级 GC 会处理它,它是一个托管资源。如果清理失败可能会损害系统功能,甚至重复的 2 级 GC 也无济于事,那么它就是非托管资源。
    【解决方案2】:

    托管资源是另一种托管类型,它实现了IDisposable。您需要在您使用的任何其他IDisposable 类型上调用Dispose()。本机资源是托管世界之外的任何东西,例如本机 Windows 句柄等。


    编辑:在评论中回答问题(评论太长)

    不,那只是托管类型。未实现 IDisposable 的正确构造类型将由垃圾收集器处理,您无需执行任何其他操作。如果您的类型直接使用本机资源(例如,通过调用 Win32 库),您必须在您的类型上实现 IDisposable 并在 Dispose 方法中处理资源。如果您的类型使用由实现IDisposable 的另一种类型封装的本机资源,则必须在您的类型的Dispose 方法中对该类型的实例调用Dispose()

    【讨论】:

    • 那么,不实现 IDisposable 的托管类型是非托管资源吗?也许我对 type verses 资源感到困惑......
    • @Yooder:你怎么能拥有一个不由类型表示的托管资源?
    • @LarryFix:不同的人使用不同的术语,但我会说一个没有要求其他东西代表它做某事的对象,直到另行通知,并且不利于他人,不持有任何资源。
    【解决方案3】:

    简短的回答是,您在 CLR 背后(到操作系统)获得的任何东西都可以称为“native”。

    • 非托管内存分配。如果您在托管类 CantStayManaged 中“新建”一块内存,则 CantStayManaged 负责释放此内存(资源)。
    • 文件、管道、事件、同步结构等的句柄 - 作为一个经验法则,如果您调用 WinAPI 来获取指向资源的指针/句柄,那么这些就是“本机资源”

    所以现在 CantStayManaged 在出价之前需要清理两件事。

    1. 托管:成员字段和 CLR 分配的任何资源。这通常等同于在“Disposable”成员对象上调用 Dispose。
    2. 不受管理:我们把所有偷偷摸摸的低级东西都放在背后。

    现在可以通过两种方式触发对象清理。

    1. Dispose(true) 案例:您在类型上显式调用了 Dispose。优秀的程序员。
    2. Dispose(false) 情况:您忘记调用 Dispose,在这种情况下,终结器应启动并仍确保正确清理。

    在这两种情况下,都应该释放非托管资源,否则会出现“泄漏!”、“崩溃!”等所有表面。 但是您应该只在 Dispose() 前一种情况下尝试清理托管资源。 在后者/终结器的情况下 - CLR 可能已经完成并收集了您的一些成员,因此您不应该访问它们(CLR 不保证最终确定对象图的顺序。)因此,您可以通过保护来避免问题使用if (AmIBeingCalledFromDispose) 保护检查进行托管清理

    HTH

    【讨论】:

    • 虽然直接持有的本机资源是非托管资源,但也有可能完全在托管代码中拥有非托管资源。拥有“资源”的对象是指在另行通知之前要求其他事物代表它做某事的对象,这可能会损害他人。如果被遗弃,GC 将无法正确清理的资源是非托管资源。
    猜你喜欢
    • 2013-02-02
    • 1970-01-01
    • 1970-01-01
    • 2016-02-15
    • 2014-07-29
    • 1970-01-01
    • 1970-01-01
    • 2018-02-20
    • 2011-03-26
    相关资源
    最近更新 更多