为什么.NET无法管理非托管内存?

为什么.NET无法管理非托管内存?,.net,garbage-collection,.net,Garbage Collection,我理解托管和非托管的区别,我也理解GC是如何工作的,所以请不要告诉我这些 让我困惑的是,当我创建并打开一个StreamReader类时。NET基本上完成了这个包装类中的所有工作,以便为我打开文件等。既然.NET做了所有肮脏的工作,为什么它不能跟踪资源,以便在StreamReader对象不再根目录时释放它们呢。换句话说,为什么我们必须实现Dispose方法并释放这些资源?为什么.NET不能为我们做这件事?.NET并没有做所有的脏活,脏活都是在.NET中完成的,这不太一样 现在,StreamRead

我理解托管和非托管的区别,我也理解GC是如何工作的,所以请不要告诉我这些


让我困惑的是,当我创建并打开一个StreamReader类时。NET基本上完成了这个包装类中的所有工作,以便为我打开文件等。既然.NET做了所有肮脏的工作,为什么它不能跟踪资源,以便在StreamReader对象不再根目录时释放它们呢。换句话说,为什么我们必须实现Dispose方法并释放这些资源?为什么.NET不能为我们做这件事?

.NET并没有做所有的脏活,脏活都是在.NET中完成的,这不太一样

现在,
StreamReader
不处理非托管内存或任何其他非托管资源。它通常处理具有非托管资源的类,尽管通常是流句柄而不是非托管内存

但为了举例,我们假设它确实如此。假设它在以下字段中有一个值:

private IntPtr _chunkOfUnmanagedMemory;
private int _lengthOfUnmanagedMemory;
好的

…为什么它不能跟踪资源

是的。有一个
IntPtr
字段正是这样做的

…这样它就可以释放他们

为什么要这样做?事实上,我会在

…一旦StreamReader对象不再是根对象

首先,在扫描到哪些对象不再扎根之前,它不会知道它不再扎根。垃圾收集可以检测立即释放的最后一个引用,但它有开销,并且在多线程场景(或者更糟糕的是,可能是多线程场景,几乎所有堆使用都可能是多线程的)中必须锁定的开销并且仍然需要一些处理循环引用的方法。因此,这不是.NET所做的,它在需要更多内存并尝试在获得更多内存之前清理一些已经拥有的内存之前,不会意识到该对象是无根的

所以这真的必须是“…一旦它被发现没有根…”

无论如何,回到:

…这样它就可以释放他们

为什么要释放他们?这可能意味着释放另一个进程正在使用的内存块。它不仅可以使相关应用程序崩溃,还可以使它通过共享内存与之通信的另一个应用程序崩溃。有两件事比使应用程序崩溃更糟糕,其中一件事是使另一个应用程序崩溃。另一种是操作系统崩溃,这也是一种可能的方式(尽管今天大多数OSs都会保护自己免受大多数此类情况的影响)

因此.NET不仅需要知道有一个指向非托管内存的指针(它确实知道),还需要知道它是对象“拥有”的,应该被释放,以及如何释放它(在
GlobalFree
HeapFree
LocalFree
VirtualFree
CoTaskMemFree
delete
free
或自定义内存管理器上的包装器来命名Windows上的一些可能性,更不用说其他操作系统了,一般来说,除了know之外,不可能知道要调用哪一个更糟糕的是,与
StreamReader
一起使用的类实际上可能会在现实生活中使用,它可能是网络套接字的句柄、文件句柄或其他东西

即使我们可以通过某种方式让代码处理所有这些问题,跨越所有操作系统中所有可能的分配/释放资源类型,这仍然会局限于已经发明的东西,这不是一个未来的限制

另一方面,有时您希望主动关闭其他可能有引用的内容。以您的
StreamReader
为例,如果从其他地方传递
,您可能会到达您希望关闭流的点,并强制其他有引用的代码检测它可以在尝试之前使用,或者抛出异常,因为任何进一步的使用都是一个bug:即使我们立即检测到无根,这可能还不够快

因此,在所有的想法中,.NET应该随意释放一堆东西是没有意义的。我们需要有一个机制来说明“如果这个对象正在被收集(因为已经发现它不再是根对象),而它还没有释放这个资源(因为它可能已经被手动完成了)”然后通过以下方式发布”,而这正是入围者所做的

由于我们可能不想等到这种情况发生,或者可能想在仍有引用的情况下进行清理,或者至少可能想通过将对象恢复到最终队列来避免减慢GC的速度,因此我们还希望程序员能够显式调用相同的清理(在某些情况下,
Close()
)也可以

我们还需要一种通过所有权链传递
Dispose()
请求的方法,这就是为什么在现实生活中,尽管
StreamReader()
没有任何非托管资源,因此不需要finalizer,但它有一个调用
Dispose()的
Dispose()
流上的
,除非
LeaveOpen
为true,否则它将进行包装


最终,如果.NET完全负责某件事情,并且知道它是否应该被清理,我们不会称之为“非托管”,因为根据定义,它将被管理。

.NET并没有做所有的脏活,脏活是在.NET中完成的,而这与.NET并不完全相同

现在,
StreamReader
不处理非托管内存或任何其他非托管资源。它通常处理类