我们在C#中有非托管资源吗?

Vin*_*zes 7 c# garbage-collection dispose unmanagedresources

我和我的朋友讨论了c#中的托管和非托管资源.

根据我的朋友说:

1.a)C#中的每个对象都是托管的,当我们用C#编码时,没有任何东西像非托管对象或资源.非托管资源概念仅适用于C++.

1.b)无论我们是否在C++中拥有托管或非托管资源,我们都需要明确地释放它.由于我们在C#中有自动垃圾收集器,因此我们不需要考虑管理资源.

据我说:

2.a)如果我们没有非托管资源那么为什么我们需要在C#中使用终结器或Dispose方法?

2.b)垃圾收集器只有关于已分配内存的信息,而不是有关资源状态的信息.因此我们需要使用dispose方法在C#中释放资源.

我需要帮助理解上述哪些参数是正确的以及有关c#中非托管资源的信息,是否存在?

提前致谢.

Han*_*ant 10

不,在不使用非托管资源的情况下编写C#程序是不可能的.不可避免的是,C#程序在100%不受管理的操作系统上运行.如果您使用文件,则使用操作系统资源.网络连接.一个帖子.控制台.Etcetera,所有非常非托管的资源.

然而,这个事实在.NET中隐藏得很好.框架库为这些本机对象提供了很好的包装类.FileStream,Socket,Thread,Console等.内存也是一个操作系统资源,垃圾收集器是它的包装器.

在所有这些资源中,只有内存资源才真正自动管理.其余的人凭借他们的包装类获得了一定程度的帮助.它们的终结器是关键,它在调用时释放操作系统资源.这非常接近自动,垃圾收集器注意到包装器类对象不再被引用,因此它释放它,终结器然后确保也释放非托管资源.

这通常很有效,您可以经常忽略代码中的这些实现细节.很多程序员都这样做.

终结器存在问题,它们需要一段时间才能开始运行.它需要一个垃圾收集来启动它们,这可能需要几毫秒到几分钟.它是不可预测的,它取决于您在代码中消耗内存的速率.如果你不使用它,那将需要很长时间.

您不能总是等待很长时间才能释放非托管资源.文件就是一个很好的例子.当你打开一个从文件中读取数据时,你真的应该在阅读完毕后关闭文件.如果你等到终结器完成该工作,那么当你需要再次打开文件时,你会冒一个程序失败的风险.您可能通过使用FileShare.None打开文件来锁定自己,它也会锁定您自己的代码.没什么大不了的:你读完后调用Close()来关闭文件.为了确保它被关闭,你应该将Close()调用放在finally块中,这样即使代码因异常而中止,它也会运行.实际上,您显式运行终结器代码.

更严重的情况是操作系统资源非常昂贵.这些的好例子是位图,它们可以占用大量非托管内存或数据库连接,它们有一个池,默认情况下只包含其中的100个.对于这些,你可以让自己很好地处理让终结器负责释放资源的情况,因为它需要太长时间.在终结器运行之前,您的程序会因异常而死亡.通常很难诊断,因为这往往只会在程序负载时发生.在很多事情发生时,总是很难调试在桌面上没有的机器上发生的问题.

.NET设计人员认识到了这一需求并设计了IDisposable接口.它的Dispose()方法旨在运行通常由终结器运行的代码,为您提供一种显式释放资源的方法,而不是等待垃圾收集器绕过它.语言设计者通过在他们的语言中添加using关键字来加入这一潮流,确保自动调用IDisposable.Dispose().

使用使用或处置()在为实现IDisposable是可选的任何对象的代码,如上面所解释,但认为很多.NET程序员是至关重要的.主要是因为每个人都没有它就开始进行.NET编程,并且当他们的程序变大时迟早会遇到问题.它甚至在调用Dispose()没有意义的类上规定,比如MemoryStream.当一个类应该实现IDisposable而不是像Thread一样,会导致精神上的痛苦.或者当一个类实现Dispose和Close时(它没有任何区别).为了进行比较,Java具有相同的注意事项,但没有IDisposable.


Tom*_*uλa 3

在 .NET 中创建的对象是托管代码,但您的对象可以保存对非托管资源的引用。垃圾收集器 (GC) 确保在不再需要托管堆上分配的任何内存时将其清除。\n但是,虽然垃圾收集器非常擅长确保内存不泄漏,但它并不能\xe2\x80\x99t了解需要释放的其他资源。例如,垃圾收集器不知道如何关闭文件句柄或如何使用 CoAllocTaskMem 等 API释放在托管堆外部分配的内存。

\n\n

管理这些类型资源的对象必须确保它们在不再需要时被释放。您可以通过重写 System.Object 的 Finalize 方法来实现此目的,该方法让垃圾收集器知道该对象愿意参与其自己的清理(在 C# 中,您使用 C++ 析构函数语法 ~MyObject,而不是直接重写该方法)。如果一个类有终结器,那么在收集该类型的对象之前,垃圾收集器将调用 object\xe2\x80\x99s 终结器,并允许它清理它可能持有的任何资源。

\n\n

该系统的一个问题是垃圾收集器无法确定性地运行,因此,在最后一次对对象的引用消失后,您的对象可能很长一段时间都无法最终确定。如果您的对象占用昂贵或稀有的资源(例如数据库连接),这可能是不可接受的。例如,如果您的对象仅打开了 10 个可用连接中的 1 个,则它应该尽快释放该连接,而不是等待垃圾收集器调用 Finalize 方法。

\n\n

为此,您应该实现一个 IDisposable 接口。阅读更多相关内容。

\n