C# 中有非托管资源吗?
我和我的朋友讨论了 C# 中的托管和非托管资源。
据我的朋友说:
1.a) C# 中的每个对象都是托管的,当我们用 C# 编写代码时,没有什么比非托管对象或资源更好的了。非托管资源概念仅由 C++ 提供。
1.b) 无论我们在 C++ 中拥有托管资源还是非托管资源,我们都需要显式释放它。由于 C# 中有自动垃圾收集器,因此我们无需考虑管理资源。
根据我的说法:
2.a)如果我们没有非托管资源,那么为什么我们需要 C# 中的终结器或 Dispose 方法?
2.b) 垃圾收集器仅具有有关已分配内存的信息,而不具有有关资源状态的信息。因此在C#中我们需要使用dispose方法来释放资源。
我需要帮助来理解上述哪些参数是正确的以及有关 c# 中非托管资源的信息,无论它们是否存在?
提前致谢。
I had a discussion with my friend about managed and unmanaged resources in c#.
According to my friend:
1.a) Every object in C# is managed and there is nothing like unmanaged object or resource when we code in C#. Unmanaged resource concept comes only with C++.
1.b) Whether we have managed or unmanaged resource in C++ we need to free it explicitly. Since we have automatic garbage collector in C# we need need not think about managing resources.
According to me:
2.a)If we do not have unmanaged resource then why do we need a finalizer or Dispose method in C#?
2.b)Garbage collector only has information about allocated memory and not about the state of the resources. Therefore we need to use dispose method to release resources in C#.
I need help in understanding which of the above arguments are correct and information about unmanaged resource in c#, whether they exist or not?
Thanks in Advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
不,不使用非托管资源就不可能编写 C# 程序。不可避免的是,C# 程序运行在 100% 非托管的操作系统上。如果您使用文件,那么您就使用了操作系统资源。网络连接。一根线。控制台。诸如此类,所有资源都非常不受管理。
然而,这一事实在 .NET 中隐藏得很好。框架库为这些本机对象提供了很好的包装类。文件流、套接字、线程、控制台等。内存也是一种操作系统资源,垃圾收集器是它的包装器。
在所有这些资源中,只有内存资源是真正自动管理的。其余的人通过他们的包装类获得了一些帮助。它们的终结器是关键,它在调用时释放操作系统资源。这非常接近自动,垃圾收集器注意到包装类对象不再在任何地方被引用,因此它释放它,终结器然后确保非托管资源也被释放。
这通常效果很好,您通常可以忽略代码中的这些实现细节。许多程序员都这样做。
不过终结器有一个问题,它们需要一段时间才能开始运行。需要进行垃圾收集才能启动它们,这可能需要几毫秒到几分钟的时间。这是不可预测的,它取决于您在代码中消耗内存的速率。如果你使用的不多,那么会需要很长时间。
您不可能总是等待那么长时间才能释放非托管资源。文件就是一个很好的例子。当您打开一个文件来从文件中读取数据时,您确实应该在读完后关闭该文件。如果您等到终结器完成该工作,那么当程序需要在一段时间后再次打开文件时,您就会面临程序失败的风险。您可能通过使用 FileShare.None 打开文件而将自己锁定在外,它也会将您自己的代码锁定在外。没什么大不了的:当你读完后,你可以调用 Close() 来关闭文件。为了确保它被关闭,您应该将 Close() 调用放在finally 块中,这样即使代码由于异常而中止,它也会运行。实际上,您显式运行终结器代码。
更严重的情况是操作系统资源非常昂贵。位图就是很好的例子,它们可以占用大量非托管内存或数据库连接,有一个用于它们的池,默认情况下仅包含其中的 100 个。对于这些,您可能会陷入这样一种情况:让终结器负责释放资源是行不通的,因为它需要太长时间。在终结器运行之前,您的程序因异常而终止。通常很难诊断,因为这种情况往往只发生在程序处于负载状态时。在不在桌面上的计算机上发生大量事情时,总是很难调试出现的问题。
.NET 设计者认识到了这一需求并设计了 IDisposable 接口。它的 Dispose() 方法旨在运行通常由终结器运行的代码,为您提供一种显式释放资源的方法,而不是等待垃圾收集器来处理它。语言设计者也加入了这一潮流,将 using 关键字添加到他们的语言中,确保自动调用 IDisposable.Dispose()。
如上所述,在代码中对任何实现 IDisposable 的对象使用 using 或 Dispose() 是可选的,但许多 .NET 程序员认为这是至关重要的。主要是因为每个人在没有它的情况下开始 .NET 编程,并且当他们的程序变大时迟早会遇到问题。它甚至被规定在调用 Dispose() 没有意义的类上,例如 MemoryStream。当一个类应该实现 IDisposable 但没有实现时,会导致精神上的痛苦,就像 Thread 一样。或者当一个类同时实现 Dispose 和 Close 时(没有区别)。作为比较,Java 有相同的考虑,但没有 IDisposable。
No, it is impossible to write a C# program without using unmanaged resources. It is inevitable, a C# program runs on an operating system that is 100% unmanaged. If you use a file then you use an operating system resource. A network connection. A thread. The console. Etcetera, all very much unmanaged resources.
That fact is however hidden pretty well in .NET. The framework library has nice wrapper classes for these native objects. FileStream, Socket, Thread, Console, etcetera. Memory is an operating system resource too, the garbage collector is a wrapper around it.
Of all of these resources, only the memory resource is truly automatically managed. The rest of them get some measure of help by virtue of their wrapper class. Their finalizer is the key, it releases the operating system resource when it is called. That's pretty close to automatic, the garbage collector notices that the wrapper class object is no longer referenced anywhere so it releases it, the finalizer then ensures that the unmanaged resource is released as well.
That usually works well, you can often ignore these implementation details in your code. Many programmers do.
There is a problem with finalizers though, they take a while to start running. It takes a garbage collection to get them started, that can take anywhere from a handful of milliseconds to minutes. It is unpredictable, it depends on the rate in which you consume memory in your code. If you don't use a lot of it then it will take a long time.
You cannot always afford to wait that long for an unmanaged resource to get released. A file is a good example. When you open one to read data from the file then you really should close the file when you're done reading. If you wait until the finalizer gets that job done then you run the risk of your program failing when it needs to open the file again, some time later. You might have locked yourself out by opening the file with FileShare.None, it locks your own code out as well. No big deal: you call Close() to close the file when you're done reading. To be sure it gets closed, you should put the Close() call in a finally block so it runs even if the code aborts due to an exception. In effect, you run the finalizer code explicitly.
A more serious case is an operating system resource that is very expensive. Good examples of those are bitmaps, they can take a lot of unmanaged memory, or database connections, there is a pool for them that by default only contains 100 of them. For these, you can get yourself well into a situation where letting the finalizer take care of releasing the resource just doesn't work because it takes too long. Your program dies on an exception before the finalizer can run. Usually pretty hard to diagnose because that tends to only happens when your program is under load. Always hard to debug problems that occur when lots of things are going on, on a machine that is not on your desktop.
The .NET designers recognized this need and designed the IDisposable interface. Its Dispose() method was designed to run the code that normally is run by the finalizer, giving you a way to explicitly releasing a resource rather than waiting for the garbage collector to get around to it. And the language designers jumped on that bandwagon by adding the using keyword to their language, ensuring that IDisposable.Dispose() is automatically called.
Using using or Dispose() in your code for any object that implements IDisposable is optional, as explained above, but considered vital by many .NET programmers. Mostly because everybody started .NET programming without it and ran into a problem sooner or later when their programs got big. It is even prescribed on classes where calling Dispose() makes no sense, like MemoryStream. And causes mental anguish when a class should implement IDisposable but doesn't, like Thread. Or when a class implements both Dispose and Close (it makes no difference). For comparison, Java has the same considerations but doesn't have IDisposable.
在 .NET 中创建的对象是托管代码,但您的对象可以保存对非托管资源的引用。垃圾收集器 (GC) 确保托管堆上分配的所有内存在不再需要时都被清除。
然而,虽然垃圾收集器非常擅长确保内存不泄漏,但它对需要释放的其他资源一无所知。例如,垃圾收集器不知道如何关闭文件句柄或如何使用 CoAllocTaskMem 等 API 释放在托管堆外部分配的内存。
管理这些类型资源的对象必须确保它们在不再需要时被释放。您可以通过重写 System.Object 的 Finalize 方法来实现此目的,该方法让垃圾收集器知道该对象愿意参与其自己的清理(在 C# 中,您使用 C++ 析构函数语法 ~MyObject,而不是直接重写该方法)。如果一个类有终结器,那么在收集该类型的对象之前,垃圾收集器将调用该对象的终结器并允许它清理它可能持有的任何资源。
该系统的一个问题是垃圾收集器无法确定性地运行,因此,在最后一次对对象的引用消失后,您的对象可能在很长一段时间内都无法最终确定。如果您的对象占用昂贵或稀有的资源(例如数据库连接),这可能是不可接受的。例如,如果您的对象仅打开了 10 个可用连接中的 1 个,则它应该尽快释放该连接,而不是等待垃圾收集器调用 Finalize 方法。
为此,您应该实现一个 IDisposable 接口。阅读更多相关内容。
Objects created in .NET are managed code but your objects can hold a reference to unmanaged ressources. Garbage collector (GC) makes sure any memory allocated on the managed heap is cleaned up after it is no longer needed.
However, while the garbage collector is great at making sure that memory does not leak, it doesn’t have any knowledge about other resources that need to be freed. For instance, the garbage collector doesn’t know how to close a file handle or how to release memory allocated outside of the managed heap with an API such as CoAllocTaskMem.
Objects that manage these types of resources must ensure that they are released when they’re no longer needed. You can accomplish this by overriding the Finalize method of System.Object, which lets the garbage collector know that the object would like to participate in its own cleanup (in C# you use the C++ destructor syntax, ~MyObject, rather than directly overriding the method). If a class has a finalizer, then before objects of that type are collected the garbage collector will call the object’s finalizer and allow it to clean up any resources that it may be holding onto.
One problem with this system is that the garbage collector does not run deterministically and, as a result, your object may not be finalized for a long time after the last reference to it has gone away. If your object holds onto an expensive or rare resource, such as a database connection, this may not be acceptable. For instance, if your object has 1 of only 10 available connections open, it should release that connection as soon as possible, rather than waiting for the garbage collector to call the finalize method.
For that you have a IDisposable interface that you should implement. Read more about it.
a) C# 中的每个对象都是托管的,当我们用 C# 编写代码时,没有什么比非托管对象或资源更好的了。非托管资源概念仅在 C++ 中出现。
这是不正确的。正如其他人提到的,我们可以从 C# 外部(例如 COM)获得非托管资源。
但是,在 C# 中当然可以拥有“非托管资源”而不访问非托管代码。从严格意义上的垃圾收集来看,这些资源可能不是不受管理的,但它们是您作为开发人员必须处理清理的资源。以线程为例:
假设附加代码返回或抛出异常。如果您不显式调用 StopThread,执行 DoLotsOfWork 的线程将不会结束,并且您的进程可能不会退出。
b) 无论我们在 C++ 中拥有托管资源还是非托管资源,我们都需要显式释放它。由于我们在 C# 中有自动垃圾收集器,因此我们不需要考虑管理资源。
我们绝对必须考虑在 C# 中管理资源。正如您所建议的,这就是 IDisposable 存在的原因。
考虑对上述代码的修改:
现在我们可以确定,无论附加代码做什么,由 Foo 类创建的线程都会在进程退出之前停止。
a) Every object in C# is managed and there is nothing like unmanaged object or resource when we code in C#. Unmanaged resource concept comes only with C++.
This is incorrect. We can have unmanaged resources from from outside C# (e.g. COM), as others have mentioned.
However, it is certainly possible to have "unmanaged resources" in C# without accessing unmanaged code. These resources may not be unmanaged in the strict sense of garbage collection, but they are resources that you, as the developer, must handle cleaning up. Take a thread for example:
Suppose the additional code returns or throws an exception. If you do not explicitly call StopThread, the thread executing DoLotsOfWork will not end, and your process will likely not exit.
b) Whether we have managed or unmanaged resource in C++ we need to free it explicitly. Since we have automatic garbage collector in C# we need need not think about managing resources.
We absolutely must consider managing resources in C#. This is why IDisposable exists, as you suggest.
Consider this modification to the above code:
Now we can be sure that no matter what the additional code does, the thread created by the Foo class will be stopped before the process exits.
您在 .net Framework 下创建的所有内容都是托管代码,因此仅由框架使用 .net Framework Manager 创建的对象消耗内存。
在 .net 框架之外创建的所有内容都是非托管代码。
当您想要释放重型对象(例如需要关闭文件或正在使用图形并且您想要)时,可以使用终结器或处置要释放与之相关的内存,您可以使用此方法。
Every thing you create under the .net Framework is Mananged code and so the memeory consume by the object created by using .net framework manager by framework only.
Every thing which get created outside the .net framwork is unmanaged code.
You can use finalizer or dispose when you want to relase the hevy object like you need to close the files or you are usning graphics and you want to release memory associated with it you can use this methods.
确实,所有在 CLR 中创建的对象都由 CLR 管理,因此您无需关心它们。
但是,当您开始使用 CLR 外部的资源(例如 COM 对象或设备上的锁)时,您有责任释放这些资源。 CLR 无法为您执行此操作,但提供了 IDisposable 接口让您编写执行清理操作的代码。
It is true that all objects created in the CLR are managed by the CLR, so you don't need to care about them.
However when you start using resources external to the CLR (a COM object for example, or a lock on a device) it is your responsibility to free those resources. The CLR cannot do it for you, but provides the
IDisposable
interface to let you write code that does the cleanup.我们确实必须处理 .NET 中的非托管资源。一个很好的例子是与数据库的连接。
我们必须显式关闭该非托管资源。这是我们在 C# 中使用
Dispose
的示例。We do have to deal with unmanaged resourses in .NET. A good example, is a connection to a database.
We have to explicitly close that unmanaged resource. This is an example of why we have
Dispose
in C#.持有非托管资源的对象会将其他实体置于某种不希望的状态(例如,使它们无法用于其他目的),直到它被告知不再需要它为止。如果在没有事先告知不再需要它的情况下就放弃它,那么其他实体将处于不希望的状态。
托管资源是一个实体,它同样将其他实体置于某种不太理想的状态,直到它被告知不再需要它为止,但如果放弃对它的所有“故意”引用,它最终会自动清理自己。
来自长寿命对象的事件订阅完全存在于托管代码中,但由于它们在长寿命对象的生命周期内不会自动清除,因此应将它们视为非托管资源。
An object which holds unmanaged resources puts other entities into a somewhat undesirable state (e.g. making them unavailable for other purposes) until it is told that it is no longer needed. If it is abandoned without first having been told that it is no longer needed, those other entities will be left in the undesirable state.
A managed resource is an entity which similarly puts other entities into a somewhat-undesirable state until it is told it is no longer needed, but it will eventually automatically clean itself up if all "deliberate" references to it are abandoned.
Event subscriptions from long-lived objects exist entirely within managed code, but because they are not cleaned up automatically during the lifetime of the long-lived objects, they should be regarded as unmanaged resources.