什么是非托管资源?
Posted
技术标签:
【中文标题】什么是非托管资源?【英文标题】:What exactly are unmanaged resources? 【发布时间】:2011-03-26 21:08:33 【问题描述】:我想了解非托管资源。 谁能给我一个基本的想法?
【问题讨论】:
另见此页面,它为正确使用 IDisposable 以及如何考虑非托管资源提供了精彩的解释和模式:***.com/questions/538060/… 【参考方案1】:非托管和托管资源基于 application domain
。
据我了解,非托管资源是用于连接到 outside of your application domain
的所有资源。
可以是 HttpClient
类,您可以使用它来获取域外的数据,也可以是 FileStream
,它可以帮助您读取/写入文件。
我们使用Using
块在我们的工作完成后立即处理这些类对象,因为GC
首先关心进程内部的资源而不是外部的资源,尽管它会最后由 GC
处理。
【讨论】:
【参考方案2】:让我们首先了解 VB6 或 C++ 程序(非 Dotnet 应用程序)是如何执行的。 我们知道计算机只能理解机器级代码。机器级代码也称为本机代码或二进制代码。因此,当我们执行VB6或C++程序时,各自的语言编译器将各自的语言源代码编译成本机代码,然后底层操作系统和硬件可以理解。
本机代码(非托管代码)特定于(本机)生成它的操作系统。如果您使用此编译后的本机代码并尝试在另一个操作系统上运行,它将失败。所以这种程序执行方式的问题在于,它不能从一个平台移植到另一个平台。
现在让我们了解 .Net 程序是如何执行的。使用 dotnet,我们可以创建不同类型的应用程序。一些常见的 .NET 应用程序类型包括 Web、Windows、控制台和移动应用程序。无论应用程序的类型如何,当您执行任何 .NET 应用程序时,都会发生以下情况
.NET 应用程序被编译成中间语言 (IL)。 IL 也称为通用中间语言 (CIL) 和 Microsoft 中间语言 (MSIL)。 .NET 和非 .NET 应用程序都生成程序集。程序集的扩展名为 .DLL 或 .EXE。例如,如果你编译一个 Windows 或控制台应用程序,你会得到一个 .EXE,而当我们编译一个 Web 或类库项目时,我们会得到一个 .DLL。 .NET 和 NON .NET 程序集的区别在于,DOTNET 程序集是中间语言格式,而 NON DOTNET 程序集是本机代码格式。
NON DOTNET 应用程序可以直接在操作系统之上运行,而 DOTNET 应用程序在称为公共语言运行时 (CLR) 的虚拟环境之上运行。 CLR 包含一个名为 Just In-Time Compiler (JIT) 的组件,它将中间语言转换为底层操作系统可以理解的本机代码。
因此,在 .NET 中,应用程序的执行包括 2 个步骤 1.语言编译器,将源代码编译成中间语言(IL) 2. CLR 中的 JIT 编译器将 IL 转换为本机代码,然后可以在底层操作系统上运行。
由于 .NET 程序集是中间语言格式而不是本机代码,因此只要目标平台具有公共语言运行时 (CLR),.NET 程序集就可以移植到任何平台。目标平台的 CLR 将中间语言转换为底层操作系统可以理解的本机代码。中级语言也称为托管代码。这是因为 CLR 管理在其中运行的代码。例如,在 VB6 程序中,开发人员负责取消分配对象消耗的内存。如果程序员忘记释放内存,我们可能会遇到难以检测到的内存不足异常。另一方面,.NET 程序员不必担心取消分配对象消耗的内存。自动内存管理,也称为抓取收集,由 CLR 提供。除了垃圾收集之外,CLR 还提供了其他几个好处,我们将在稍后的会议中讨论这些好处。由于 CLR 管理和执行中间语言,因此它 (IL) 也称为托管代码。
.NET 支持不同的编程语言,如 C#、VB、J# 和 C++。 C#、VB 和 J# 只能生成托管代码 (IL),而 C++ 可以生成托管代码 (IL) 和非托管代码(本机代码)。
本机代码不会永久存储在任何地方,在我们关闭程序后,本机代码会被丢弃。当我们再次执行程序时,又会生成原生代码。
.NET 程序类似于 java 程序的执行。在 java 中我们有字节码和 JVM(Java 虚拟机),而在 .NET 中我们有中间语言和 CLR(公共语言运行时)
这是从此链接提供的 - 他是一位出色的导师。 http://csharp-video-tutorials.blogspot.in/2012/07/net-program-execution-part-1.html
【讨论】:
【参考方案3】:一些用户在托管资源中对打开的文件、数据库连接、分配的内存、位图、文件流等进行排名,而另一些则在非托管资源中排名。 那么它们是托管的还是非托管的?
我的看法是,响应更复杂:当您在 .NET 中打开文件时,您可能会使用一些内置的 .NET 类 System.IO.File、FileStream 或其他东西。因为它是一个普通的 .NET 类,所以它是托管的。但它是一个包装器,它在内部执行真正打开文件的“脏工作”(使用 Win32 dll 与操作系统通信,调用低级函数甚至汇编程序指令)。这就是 .NET 所不知道的,非托管的。 但是您也许可以使用汇编指令自行打开文件并绕过 .NET 文件功能。那么句柄和打开的文件都是非托管资源。
与 DB 相同:如果您使用一些 DB 程序集,您有像 DbConnection 等类,它们是 .NET 已知的并被管理。但是他们包装了“肮脏的工作”,这是不受管理的(在服务器上分配内存,与它建立连接,......)。 如果你不使用这个包装类,自己打开一些网络套接字并使用一些命令与你自己奇怪的数据库通信,它是非托管的。
这些包装类(文件、DbConnection 等)是托管的,但它们在内部使用非托管资源的方式与您一样,如果您不使用包装器并自己做“脏活”的话。因此这些包装器确实实现了 Dispose/Finalize 模式。他们有责任允许程序员在不再需要包装器时释放非托管资源,并在包装器被垃圾回收时释放它们。包装器会被垃圾收集器正确地垃圾收集,但内部的非托管资源将使用 Dispose/Finalize 模式进行收集。
如果您不使用内置的 .NET 或 3rd 方包装类并在您的类中通过一些汇编指令等打开文件,则这些打开的文件是非托管的,您必须实现 dispose/finalise 模式。如果你不这样做,即使你不再使用它(文件操作完成)或者甚至在你的应用程序终止之后,也会出现内存泄漏、永远锁定的资源等。
但您的责任也在于使用这些包装器。对于那些实现 dispose/finalise(你认识他们,他们实现 IDisposable)的人,也实现你的 dispose/finalise 模式并 Dispose 甚至这些包装器或给他们信号以释放他们的非托管资源。如果不这样做,资源将在一段时间后无限期释放,但立即释放它是干净的(立即关闭文件,不要让它打开并随机阻塞几分钟/小时)。因此,在您的类的 Dispose 方法中,您调用所有使用过的包装器的 Dispose 方法。
【讨论】:
关于unmanaged vs managed resources
的额外清晰度很好
感谢您的回答。建议我们实际在哪些类上调用 Dispose?
这很简单。在您使用的每个类上,您必须验证它是否实现了 IDisposable 接口。如果是,那么如果您在一种方法中使用此类(例如:打开文件、存储文本、关闭文件),您可以使用 using() 模式,它会自动为您调用 Dispose。如果你在更多方法中使用这样的类(例如:你的类包含文件,在构造函数中打开文件,然后几个方法添加一些日志......),那么你必须通过你的类实现 IDisposable 接口,实现 Dispose/Finalize 模式并正确处理该类的对象。
"...一些内置的.NET类System.IO.File、FileStream之类的。因为是普通的.NET类,所以是托管的。"恕我直言,这是错误的和具有误导性的。它们不受管理。如果它们是托管的,那么您可以分配这些类并期望垃圾收集器以确定的方式完全处理所有资源的释放。但是,这将导致文件句柄和非托管资源被锁定并持有比必要的更长的时间,因为垃圾收集器不会释放类,并且可能很长时间都不会最终确定它。
@AaronLS 在您的评论中,您谈到了“FileStream”,将其称为非托管但它不是,尽管它在内部使用非托管资源来完成其工作。在托管世界中,微软隐藏了很多通过实现 dispose 模式从您那里收集非托管的东西。托管代码并不意味着它不使用非托管资源。但是,Microsoft 在这些类型的对象上实现 IDisposable 方面做得很好。它实现了 IDisposable 的事实证明了这一点。关于该证据,我们应该将其视为托管对象。【参考方案4】:
在 .NET 托管堆中为其分配内存的任何资源都是托管资源。 CLR 完全了解这种内存,并将尽一切努力确保它不会成为孤立的。其他任何事情都是不受管理的。例如与 COM 互操作,可能会在进程内存空间中创建对象,但 CLR 不会处理它。在这种情况下,跨托管边界进行调用的托管对象应该对超出它的任何事情负责。
【讨论】:
【参考方案5】:“非托管资源”不是事物,而是责任。如果一个对象拥有非托管资源,这意味着(1)它外部的某个实体已被以一种如果不清理可能导致问题的方式进行操作,并且(2)该对象具有执行此类清理所需的信息并负责这样做。
尽管许多类型的非托管资源都与各种类型的操作系统实体(文件、GDI 句柄、分配的内存块等)密切相关,但没有任何一种实体可以由所有这些实体共享,除了清理的责任。通常,如果一个对象有责任执行清理,它将有一个 Dispose 方法,指示它执行它负责的所有清理。
在某些情况下,对象会考虑到在没有人先调用 Dispose 的情况下它们可能被丢弃的可能性。 GC 允许对象请求它们已被放弃的通知(通过调用名为 Finalize 的例程),并且对象可以使用此通知来执行自己的清理。
不幸的是,“托管资源”和“非托管资源”等术语被不同的人用来表示不同的事物;坦率地说,将对象视为没有任何清理责任,只有在调用 Dispose 时才会处理的清理责任,或者应该通过 Dispose 处理的清理责任,但可以也由 Finalize 处理。
【讨论】:
【参考方案6】:托管资源基本上是指由垃圾收集器管理的“托管内存”。当您不再有对托管对象(使用托管内存)的任何引用时,垃圾收集器将(最终)为您释放该内存。
非托管资源就是垃圾收集器不知道的一切。例如:
打开文件 打开网络连接 非托管内存 在 XNA 中:顶点缓冲区、索引缓冲区、纹理等。通常,您希望在丢失对管理它们的对象的所有引用之前释放这些非托管资源。为此,您可以在该对象上调用 Dispose
,或者(在 C# 中)使用将为您处理调用 Dispose
的 using
语句。
如果您正确地忽略了Dispose
您的非托管资源,垃圾收集器最终会在包含该资源的对象被垃圾收集时为您处理它(这是“终结”)。但是由于垃圾收集器不知道非托管资源,它无法判断释放它们需要多严重 - 因此您的程序可能会表现不佳或完全耗尽资源。
如果您自己实现一个处理非托管资源的类,则由您来正确实现Dispose
和Finalize
。
【讨论】:
开放数据库连接属于哪个类别?托管/非托管? +1 其他答案错过了重要的一点,即您在 managed 对象上调用 Dispose,该对象在内部处理释放它所包装的非托管资源(例如文件句柄、GDI+位图,...),如果您直接访问非托管资源(PInvoke 等),您需要处理它。 @Dev:非托管 - 因为 GC 不知道它(假设您没有使用一些假设的托管内存数据库)。但连接对象本身可能不包含非托管资源。据推测,数据库连接使用打开的文件或网络连接somewhere - 但可能另一个对象(连接对象除外)正在处理该非托管资源(可能是您的数据库库缓存连接)。检查文档并查看它要求您致电Dispose
或使用using
。
我对此有一个基本的评论/问题,我能否仅通过类型将对象关联为托管/非托管,例如,字符串是托管的,DataSet 是非托管的(这就是为什么它有一个 Dispose() 方法),数据库连接是非托管的(因为它们有 dispose)等等。假设如果它有一个“Dispose()”方法,那么它是非托管的吗?除此之外,XmlDocument 对象是什么?谢谢
@ganders 这是一个很好的经验法则。尽管请注意所有 C# 类实例都是托管对象。如果一个类的实例可以拥有非托管资源,那么该类应该实现IDisposable
。如果一个类确实实现了IDisposable
,那么您应该在处理完该类的实例后用using
或Dispose()
处理它们。基于此,您的相反观点成立:如果一个类实现了IDisposable
,那么它可能在内部拥有非托管资源。【参考方案7】:
非托管资源是在 .NET 运行时 (CLR) 之外运行的资源(也称为非 .NET 代码)。例如,调用 Win32 API 中的 DLL,或调用用 C++ 编写的 .dll。
【讨论】:
【参考方案8】:托管资源和非托管资源之间的基本区别在于 垃圾收集器在某个时间点了解所有托管资源 GC 将出现并清理所有相关的内存和资源 带有托管对象。 GC 不知道非托管资源,例如 作为文件,流和句柄,所以如果你没有明确地清理它们 那么你的代码最终会导致内存泄漏和资源锁定。
盗自here,欢迎阅读全文。
【讨论】:
以上是关于什么是非托管资源?的主要内容,如果未能解决你的问题,请参考以下文章