122

我有以下代码:

MemoryStream foo(){
    MemoryStream ms = new MemoryStream();
    // write stuff to ms
    return ms;
}

void bar(){
    MemoryStream ms2 = foo();
    // do stuff with ms2
    return;
}

我分配的 MemoryStream 是否有可能在以后无法处理?

我有一个同行评审坚持要我手动关闭这个,我找不到信息来判断他是否有一个有效的观点。

4

12 回答 12

179

你不会泄露任何东西——至少在当前的实现中是这样。

调用 Dispose 不会更快地清理 MemoryStream 使用的内存。它将阻止的流在调用后对读/写调用可行,这可能对您有用,也可能没有用。

如果您绝对确定您永远不想从 MemoryStream 移动到另一种流,那么不调用 Dispose 不会对您造成任何伤害。但是,这通常是一种很好的做法,部分原因是如果您确实更改为使用不同的 Stream,您不希望被难以找到的错误所困扰,因为您很早就选择了简单的方法。(另一方面,还有 YAGNI 论点……)

这样做的另一个原因是新的实现可能会引入资源,这些资源将在 Dispose 上释放。

于 2008-10-24T16:28:15.987 回答
64

如果某些东西是一次性的,则应始终将其丢弃。你应该using在你的方法中使用一个语句bar()来确保ms2得到 Disposed。

它最终会被垃圾收集器清理掉,但调用 Dispose 总是好的做法。如果您在代码上运行 FxCop,它会将其标记为警告。

于 2008-10-24T15:42:46.750 回答
30

是的,泄漏,这取决于您如何定义 LEAK 以及您的意思是多长时间...

如果泄漏的意思是“内存仍然分配,无法使用,即使您已经完成使用它”,而后者是指在调用 dispose 后的任何时间,那么是的,可能存在泄漏,尽管它不是永久性的(即应用程序运行时的生命周期)。

要释放 MemoryStream 使用的托管内存,您需要取消对它的引用,方法是取消对它的引用,这样它就可以立即进行垃圾回收。如果你没有这样做,那么你会在你用完它之后创建一个临时泄漏,直到你的引用超出范围,因为在此期间内存将无法用于分配。

using 语句(而不是简单地调用 dispose)的好处是您可以在 using 语句中声明您的引用。当 using 语句完成时,不仅调用 dispose,而且您的引用超出范围,有效地使引用无效并使您的对象立即有资格进行垃圾回收,而无需您记住编写“reference=null”代码。

虽然未能立即取消引用某些内容并不是经典的“永久性”内存泄漏,但它肯定具有相同的效果。例如,如果您保留对 MemoryStream 的引用(即使在调用 dispose 之后),并且在您的方法中再往下一点,您尝试分配更多内存......您仍然引用的内存流正在使用的内存将不可用直到你取消引用或者它超出范围,即使你调用了 dispose 并完成了使用它。

于 2010-05-14T20:33:03.983 回答
9

这已经得到了回答,但我只想补充一点,信息隐藏的老式原则意味着您可能在将来的某个时候想要重构:

MemoryStream foo()
{    
    MemoryStream ms = new MemoryStream();    
    // write stuff to ms    
    return ms;
}

到:

Stream foo()
{    
   ...
}

这强调了调用者不应该关心返回的 Stream 类型,并且可以更改内部实现(例如,在模拟单元测试时)。

如果您没有在 bar 实现中使用 Dispose,那么您将遇到麻烦:

void bar()
{    
    using (Stream s = foo())
    {
        // do stuff with s
        return;
    }
}
于 2009-05-15T20:01:18.880 回答
8

不需要调用.Dispose()(或用 包装)。Using

您调用的原因.Dispose()是尽快释放资源

想想,比如说,堆栈溢出服务器,我们有一组有限的内存和数千个请求进入。我们不想等待预定的垃圾收集,我们希望尽快释放内存以便它可用对于新的传入请求。

于 2008-10-24T15:45:15.683 回答
5

所有流都实现 IDisposable。将你的内存流包装在 using 语句中,你会很好而且花花公子。using 块将确保您的流无论如何都关闭和处置。

无论你打电话给 Foo 你都可以使用(MemoryStream ms = foo()) ,我认为你应该还可以。

于 2008-10-24T15:42:02.523 回答
3

我建议将 MemoryStream 包装bar()在一个using语句中,主要是为了保持一致性:

  • 现在 MemoryStream 不会释放内存.Dispose(),但有可能在将来的某个时候它可能会,或者您(或您公司的其他人)可能会用您自己的自定义 MemoryStream 替换它,等等。
  • 它有助于在您的项目中建立一种模式,以确保所有流都得到处理——通过说“必须处理所有流”而不是“必须处理某些流,但某些流不必”来更明确地划定界限...
  • 如果您曾经更改代码以允许返回其他类型的 Stream,则无论如何都需要将其更改为 dispose。

在创建和返回 IDisposable等情况下,我通常会做的另一件事foo()是确保构造对象与return被异常捕获之间的任何失败、释放对象并重新抛出异常:

MemoryStream x = new MemoryStream();
try
{
    // ... other code goes here ...
    return x;
}
catch
{
    // "other code" failed, dispose the stream before throwing out the Exception
    x.Dispose();
    throw;
}
于 2008-11-23T08:35:16.520 回答
2

您不会泄漏内存,但您的代码审查员正确地指出您应该关闭您的流。这样做是有礼貌的。

唯一可能泄漏内存的情况是您不小心留下了对流的引用并且从不关闭它。您仍然没有真正泄漏内存,但是您不必要地延长了您声称使用它的时间。

于 2008-10-24T16:22:23.333 回答
1

如果一个对象实现了 IDisposable,您必须在完成后调用 .Dispose 方法。

在某些对象中,Dispose 与 Close 的含义相同,反之亦然,在这种情况下,两者都可以。

现在,对于您的特定问题,不,您不会泄漏内存。

于 2008-10-24T15:42:23.960 回答
-1

我不是.net 专家,但也许这里的问题是资源,即文件句柄,而不是内存。我猜垃圾收集器最终会释放流并关闭句柄,但我认为明确关闭它始终是最佳实践,以确保将内容刷新到磁盘。

于 2008-10-24T15:44:37.120 回答
-2

在垃圾收集语言中,非托管资源的处置是不确定的。即使您显式调用 Dispose,您也绝对无法控制实际释放后备内存的时间。Dispose 在对象超出范围时被隐式调用,无论是通过退出 using 语句,还是从从属方法弹出调用堆栈。话虽如此,有时对象实际上可能是托管资源(例如文件)的包装器。这就是为什么最好在 finally 语句中显式关闭或使用 using 语句的原因。干杯

于 2009-05-15T15:58:41.397 回答
-3

MemoryStram 只不过是字节数组,它是托管对象。忘记 dispose 或 close 这除了超出最终确定之外没有任何副作用。
只需检查反射器中 MemoryStream 的构造函数或刷新方法,就会清楚为什么您不需要担心关闭或处置它,而不仅仅是遵循良好实践。

于 2013-01-08T08:51:19.960 回答