到底什么是非托管资源?


Answers:


177

托管资源基本上是指由垃圾回收器管理的“托管内存”。当您不再对托管对象(使用托管内存)进行任何引用时,垃圾收集器将(最终)为您释放该内存。

那么,非托管资源就是垃圾收集器所不知道的一切。例如:

  • 打开文件
  • 打开网络连接
  • 非托管内存
  • 在XNA中:顶点缓冲区,索引缓冲区,纹理等。

通常,丢失对管理它们的对象的所有引用之前,您要释放这些非托管资源。您可以通过调用Dispose该对象或(使用C#)using调用Dispose将为您处理的语句来执行此操作。

如果您Dispose正确地忽略了非托管资源,那么当包含该资源的对象被垃圾收集时,垃圾收集器将最终为您处理(这是“完成”)。但是,由于垃圾收集器不了解非托管资源,因此无法判断释放它们的严重程度-因此,程序性能可能很差或完全耗尽资源。

如果您自己实现一个处理非托管资源的类,则由您自己实现DisposeFinalize正确执行。


7
开放式数据库连接属于哪个类别?托管/非托管?
Deviprasad Das

8
+1其他答案错过了要点,即您在内部处理释放包装的非托管资源(例如,文件句柄,GDI +位图等)的托管对象上调用Dispose ,并且如果直接访问非托管资源( PInvoke等),您需要进行处理。
伊恩·默瑟

2
@Dev:非托管-由于GC不了解它(假设您没有使用某些假设的托管内存数据库)。但是连接对象本身可能不保存非托管资源。据推测,该数据库连接使用一个打开的文件或网络连接的地方 -但它可能是另一个对象(除连接对象除外)正在处理非托管资源(也许是你的数据库的库缓存连接)。查看文档,看看在哪里要求您致电Dispose或使用using
安德鲁·罗素

11
我对此有一个基本的注释/问题,是否可以仅通过类型将对象关联为托管/非托管对象,例如,字符串是托管对象,DataSet是非托管对象(这就是为什么它具有Dispose()方法的原因) ,数据库连接处于非托管状态(因为它们已经进行了处置)等。假设是假的,如果它具有“ Dispose()”方法,那么它处于非托管状态?除此之外,XmlDocument对象是什么?谢谢
甘德斯2012年

15
@ganders这是一个很好的经验法则。尽管要注意所有C#类实例都是托管对象。如果某个类的实例可以容纳非托管资源,则该类实现IDisposable。如果某个类确实实现IDisposable,那么您应该在处理完该类的实例时usingDispose()使用它们时将其丢弃。基于此,您可以反过来成立:如果一个类实现IDisposable,则它可能在内部持有非托管资源。
安德鲁·罗素

56

有些用户将打开的文件,数据库连接,分配的内存,位图,文件流等在托管资源之间进行排名,而其他用户则在非托管资源之间进行排名。那么他们是受管理的还是不受管理的?

我的看法是,响应更加复杂:在.NET中打开文件时,您可能会使用一些内置的.NET类System.IO.File,FileStream或其他名称。因为它是普通的.NET类,所以可以对其进行管理。但这是一个包装程序,它实际上在内部进行“脏工作”(使用Win32 dll与操作系统进行通信,调用低级函数甚至是汇编程序指令),从而真正打开了文件。这就是.NET不了解的内容,不受管理。但是您也许可以使用汇编程序指令自己打开文件,并绕过.NET文件功能。然后,句柄和打开文件是非托管资源。

与DB相同:如果使用某些DB程序集,则具有DbConnection等类,它们是.NET已知的和托管的。但是他们包装了“脏活”,这是不受管理的(在服务器上分配内存,建立与之的连接,...)。如果您不使用此包装器类并自己打开一些网络套接字,并使用一些命令与您自己的奇怪数据库进行通信,则它将不受管理。

这些包装器类(文件,DbConnection等)是托管的,但是如果您不使用包装器并且自己做“脏活”,它们内部将以与您相同的方式使用非托管资源。因此,这些包装器确实实现了Dispose / Finalize模式。他们的责任是允许程序员在不再需要包装器时释放非托管资源,并在包装​​器被垃圾回收时释放它们。包装器将由垃圾收集器正确地进行垃圾收集,但是内部的非托管资源将通过使用Dispose / Finalize模式进行收集。

如果您不使用内置的.NET或第三方包装器类,而是通过类中的某些汇编程序指令等打开文件,则这些打开的文件将不受管理,并且您必须实现处理/完成模式。如果不这样做,那么即使您不再使用它(文件操作已完成),甚至在应用程序终止后,也都将发生内存泄漏,永久锁定的资源等情况。

但是,使用这些包装程序时,您也要承担责任。对于那些实现了处理/完成(您认识到它们,他们实现了IDisposable)的对象,还实现了您的处理/完成模式,甚至处理了这些包装器,或者发出了释放其非托管资源的信号。如果不这样做,则资源将在无限期的释放后被释放,但是立即释放它是干净的(立即关闭文件,并且不要在几分钟/几小时内不打开并阻止它)。因此,在类的Dispose方法中,调用所有使用的包装器的Dispose方法。


1
好人就更清晰了unmanaged vs managed resources
现在他一定不能被任命。

谢谢你的回答。建议我们实际调用Dispose在哪个类上?
BKSpurgeon '16

2
这很简单。在使用的每个类上,必须验证它是否实现IDisposable接口。如果是,那么如果您在一种方法中使用此类(例如:打开文件,存储文本,关闭文件),则可以使用using(){}模式,该模式会自动为您调用Dispose。如果您在更多方法中使用此类(例如:您的类包含File,在构造函数中打开文件,然后几个方法添加一些日志...),那么您必须通过类实现IDisposable接口,实现Dispose / Finalize模式并正确处置该类的对象。
马塔斯(Martas)

1
“ ...一些内置的.NET类System.IO.File,FileStream或其他。由于它是普通的.NET类,因此可以对其进行管理。” 尊重地说,这是错误和误导的。他们没有得到管理。如果对它们进行了管理,则可以分配这些类,并期望垃圾收集器以确定性方式完全处理所有资源的释放。但是,这将导致文件句柄和非托管资源被锁定和保留的时间比必要的时间长得多,这是因为垃圾回收器不会释放该类,也不会在很长时间内将其完成。
AaronLS '16

1
@AaronLS在您的评论中提到了“ FileStream”,将其称为非托管,但不是,尽管它在内部使用非托管资源来完成其工作。在托管世界中,Microsoft通过实施布置图案。托管代码并不意味着它不使用非托管资源。但是,Microsoft在对这些类型的对象实施IDisposable方面做得很好。它实现了IDisposable的事实证明了这一点。关于该证据,我们应将其视为受管对象。
马里克·哈利勒

12

非托管资源是在.NET运行时(CLR)(也称为非.NET代码)之外运行的资源。例如,对Win32 API中的DLL的调用或对用C ++编写的.dll的调用。


6

“非托管资源”不是事情,而是责任。如果对象拥有非托管资源,则意味着(1)它以外的某个实体已经以某种方式操作,如果不清理可能会引起问题,并且(2)该对象具有执行此类清理所必需的信息并负责为做到这一点。

尽管许多类型的非托管资源与各种类型的操作系统实体(文件,GDI句柄,分配的内存块等)都具有非常强的关联性,但除了它们的职责外,没有一种类型的实体可以由所有它们共享清理。通常,如果一个对象有责任执行清理,则它将具有Dispose方法,该方法指示该对象执行它负责的所有清理。

在某些情况下,对象会考虑在没有任何人首先调用“处置”的情况下将其丢弃的可能性。GC允许对象请求已被废弃的通知(通过调用名为Finalize的例程),并且对象可以使用此通知自行执行清理。

不幸的是,诸如“托管资源”和“非托管资源”之类的术语被不同的人用来表示不同的事物。坦白地说,从对象的角度考虑,要么没有任何清理责任,要么只有在调用Dispose时才负责清理责任,要么有应该通过Dispose来负责清理责任,但可以也由Finalize照顾。


5

托管资源和非托管资源之间的基本区别是,垃圾收集器知道所有托管资源,在某个时间点,GC会来清理所有与托管对象相关联的内存和资源。GC不了解非托管资源,例如文件,流和句柄,因此,如果不在代码中显式清除它们,则会导致内存泄漏和资源锁定。

这里被盗,可以随时阅读整篇文章。


2

在.NET托管堆中为其分配内存的任何资源都是托管资源。CLR完全了解这种内存,并将尽一切努力确保它不会被孤立。其他所有内容均不受管理。例如,与COM互操作可能会在过程存储空间中创建对象,但CLR不会处理它。在这种情况下,跨托管边界进行调用的托管对象应对超出其范围的所有内容负责。


0

让我们首先了解如何使用VB6或C ++程序(非Dotnet应用程序)执行。我们知道计算机只理解机器级别的代码。机器级代码也称为本地或二进制代码。因此,当我们执行VB6或C ++程序时,相应的语言编译器会将相应的语言源代码编译为本机代码,然后可由底层操作系统和硬件理解。

本机代码(非托管代码)特定于(生成本机的)操作系统。如果您使用此已编译的本机代码并尝试在其他操作系统上运行,它将失败。因此,这种程序执行方式的问题在于,它不能从一个平台移植到另一平台。

现在让我们了解一下.Net程序如何执行。使用dotnet,我们可以创建不同类型的应用程序。.NET应用程序的几种常见类型包括Web,Windows,控制台和移动应用程序。无论应用程序的类型如何,当您执行任何.NET应用程序时,都会发生以下情况

  1. .NET应用程序被编译成中间语言(IL)。IL也称为通用中间语言(CIL)和Microsoft中间语言(MSIL)。.NET和非.NET应用程序均会生成程序集。程序集的扩展名为.DLL或.EXE。例如,如果您编译Windows或控制台应用程序,则会得到一个.EXE,而当我们编译Web或Class库项目时,就会得到一个.DLL。.NET和NON .NET程序集之间的区别在于,DOTNET程序集采用中间语言格式,而NON DOTNET程序集则采用本机代码格式。

  2. NON DOTNET应用程序可以直接在操作系统之上运行,而DOTNET应用程序可以在称为公共语言运行时(CLR)的虚拟环境之上运行。CLR包含一个称为即时编译器(JIT)的组件,该组件会将中间语言转换为基础操作系统可以理解的本机代码。

因此,在.NET中,应用程序执行包括2个步骤:1.语言编译器,将源代码编译为中间语言(IL)。2. CLR中的JIT编译器将IL转换为本地代码,然后可以在基础操作系统上运行。

由于.NET程序集采用Intermedaite语言格式而不是本机代码,因此只要目标平台具有公共语言运行时(CLR),. NET程序集便可以移植到任何平台。目标平台的CLR将Intermedaite语言转换为底层操作系统可以理解的本机代码。中间语言也称为托管代码。这是因为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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.