首页 > 解决方案 > 从类创建的角度来看,托管资源与非托管资源

问题描述

我想了解的是,当我创建自己的类时,我怎么知道什么是托管资源和非托管资源,所以我知道我的类是否需要提供清理它的能力,或者 GC 是否最终会这样做。此外,更深入一点,当我创建一个 .Dispose() 方法时,将有一个用于托管资源的块和一个用于非托管资源的块,以及我如何知道哪些资源应该在哪个块中清理。

我已经阅读了许多关于 C# 程序中托管资源与非托管资源的答案,但其中大多数都提供了关于 GC 清理的定义,如“托管资源由 GC 清理而非托管资源不是”。这对我没有帮助,因为我看不到 GC 如何确定它将清理什么以及它将留下什么。我也明白,如果一个类提供了一个 .Dispose() 方法,我的程序应该执行它。

我看到的答案表明,如果我使用 WIN32 API,我就创建了一个非托管资源。如果我不调用 WIN32 API,这是否意味着我没有任何非托管资源?我也偶然发现了马歇尔。Marshall 是否也创建非托管资源?是否有其他“关键字/类”可用于识别我正在创建非托管资源?

请从您的答案中排除有关“占用大量内存的托管资源”的任何内容。我知道提供释放此内存的能力会很好,但这不是必需的,因为 GC 最终会这样做,只是并不总是及时。

标签: c#garbage-collection

解决方案


通常,如果您没有跨越本机代码和托管代码的界限,则不必费心在类中释放非托管资源。

当您运行 .NET 应用程序时,框架会在内存中为其分配一个托管切片,您可以从 .NET 框架访问的几乎所有内容都将被 GC 存储和跟踪。其他一切都在这个切片之外,没有 GC 的敏锐眼睛。

因此,对于您关于 GC 如何确定应该收集哪些资源以及不应该收集哪些资源的问题,简短的回答是它对非托管资源一无所知,因此它也无法收集它们。

这些世界——原生世界和被管理世界——是分开的,但它们可以相互交流,这就是编组的目的。你可以在这里阅读更多关于它的信息。当然,您可以创建非托管资源,但这并不意味着您每次使用它时都会这样做。

说每次使用 Win32 API 时都会创建必须释放的本机资源,这也有点极端。当您在任何本机代码上使用Platform InvokeC++/CLI 包装器调用创建指针或任何应在本机世界中手动释放的内容时(这些当然不会被 GC 跟踪),您必须手动释放它们,如果它们尚未由本机方发布。但是,如果您使用仅适用于原始类型的 API,那么您无需发布任何内容。

如果你没有使用上面的任何东西,那么你很有可能不必为直接释放任何非托管的东西而准备你的类。

有些类型使用本机资源——你可能已经遇到过——它们是底层的托管包装器。他们Dispose通过编组在实现中释放这些资源。

例如,FileStream托管类持有给定文件的非托管句柄。它FileStream本身是由 GC 跟踪和收集的托管类,但非托管句柄不是,它必须手动释放,因此如果您,用户FileStream没有在代码中调用其Dispose方法,则该句柄将保留在内存中泄漏,直到应用程序退出。


推荐阅读