我记得我是通过将图像从网络直接流式传输到位图中来加载图像的。关闭流,返回位图并将其保存在图像控件中。
当我执行 = loadPicture() 时,我例外,第一个位图会像 C++ 中的智能指针一样被释放。但它没有,我消耗了很多内存,直到我打电话给处置。所以我的问题是。
GC 和 Dispose 对象如何在 C# 中工作?为什么它不像 smart_ptr 那样实现?
我记得我是通过将图像从网络直接流式传输到位图中来加载图像的。关闭流,返回位图并将其保存在图像控件中。
当我执行 = loadPicture() 时,我例外,第一个位图会像 C++ 中的智能指针一样被释放。但它没有,我消耗了很多内存,直到我打电话给处置。所以我的问题是。
GC 和 Dispose 对象如何在 C# 中工作?为什么它不像 smart_ptr 那样实现?
引用不是智能指针。让引用变量超出范围,将其替换为另一个值,和/或将其设置为 null 都无济于事。
这只是 CLI /GC 设计的一部分......
Gargage Collection (GC) 将在需要时运行,并且应该清理使用的托管内存,以及(如果提供了终结器)任何非托管资源。但是对于确定性清理:这就是IDisposable
. Dispose()
当您完成这些对象时,您的工作就是处理它们 - 要么通过using
,要么将其交给承担此责任的其他东西(常见的,例如,流/阅读器等)。
using (StreamReader reader = new StreamReader(myfile)))
{
...
}
当运行时认为有必要时,GC 就会启动。
基本规则是:当您使用 Disposable 类型(IDispose)时,您(作为程序员)应该尽快释放该类型使用的资源,在不再需要使用该类型时调用 Dispose。例如,当您读取文件时,您会在完成读取后立即关闭该文件。(在这种情况下调用 close 也会调用 dispose)。
您必须在任何实现 IDisposable 的对象上显式调用 Dispose,否则您的非托管资源将不会被释放。如果你不想显式调用它,那么你必须重写 Finalize 方法来调用 Dispose 方法——这就是为什么你会经常看到这个:
class MyClass : IDisposable
{
...
~MyClass()
{
this.Dispose(false);
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{ /* dispose managed stuff also */ }
/* but dispose unmanaged stuff always */
}
}
smart_ptr 被引用计数。虽然这允许在它们不再被任何代码引用时确定性地释放它们的资源,但它们确实有自己的问题:分配引用总是需要更新计数器,循环引用无法自动释放导致内存泄漏,内存管理器被更频繁地调用。
.NET 中的 GC 是一个全面的收集器。它从感觉应该释放内存的任何时间开始(通常由某些内存使用条件触发,但不是确定性的),并从构建系统中所有活动引用的列表开始(包括 CPU 寄存器中的引用、嵌套引用等) .)。这是有效的,因为我们处于无法进行指针运算等的托管环境中 - 系统可以跟踪所有引用。在构建了实时引用列表之后,它基本上会释放所有不知道不再使用的内存。当然,这只是一个基本的草图,对于非托管资源的效率和管理,还有更多的东西,比如对象生成、终结器等,但这对于基本理解它的工作原理并不重要。
IDisposable 接口用于实现一次性模式,这有助于您处理应以确定方式处置的对象。该模式是当不再需要对象时显式调用 Dispose() ,因此释放非托管资源或关闭句柄等,但不释放其内存。这将在稍后由 GC 完成,但稍后发生这并不重要,因为已经执行了资源的确定性释放。