如果.NET中的MemoryStream未关闭,是否会创建内存泄漏?

时间:2008-10-24 15:39:40

标签: c# .net memory-leaks memorystream

我有以下代码:

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

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

我分配的MemoryStream是否有可能以后无法被丢弃?

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

12 个答案:

答案 0 :(得分:153)

至少在当前的实施中,你不会泄漏任何东西。

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

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

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

答案 1 :(得分:57)

如果某些东西是Disposable,你应该总是处理它。您应该在bar()方法中使用using语句以确保ms2获得Disposed。

它最终会被垃圾收集器清理干净,但Dispose始终是一个好习惯。如果您在代码上运行FxCop,它会将其标记为警告。

答案 2 :(得分:24)

是的 a 泄漏,具体取决于您如何定义LEAK以及您的意思是多少......

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

要释放MemoryStream使用的托管内存,需要取消引用它,通过取消对它的引用,使其立即符合垃圾回收的条件。如果你没有这样做,那么你从使用它之后就会创建一个临时的泄漏,直到你的引用超出范围,因为在此期间内存将无法分配。

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

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

答案 3 :(得分:8)

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

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

为:

Stream foo()
{    
   ...
}

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

如果您未在条形实现中使用Dispose,则需要遇到麻烦:

void bar()
{    
    using (Stream s = foo())
    {
        // do stuff with s
        return;
    }
}

答案 4 :(得分:5)

所有流都实现了IDisposable。在一个using语句中包装你的内存流,你会很好,花花公子。使用块将确保您的流无论如何都会被关闭和处理。

无论你在哪里调用Foo,你都可以使用(MemoryStream ms = foo()),我认为你应该还可以。

答案 5 :(得分:5)

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

您致电.Dispose()的原因是尽快释放资源

考虑一下Stack Overflow服务器,我们有一组有限的内存和数千个请求。我们不想等待计划的垃圾收集,我们希望尽快释放该内存所以它可用于新的传入请求。

答案 6 :(得分:2)

您不会泄漏内存,但您的代码审核者指出您应该关闭您的信息流是正确的。这样做很有礼貌。

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

答案 7 :(得分:2)

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

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

在创建和返回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;
}

答案 8 :(得分:1)

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

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

现在,对于你的特定问题,不,你不会泄漏记忆。

答案 9 :(得分:-1)

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

答案 10 :(得分:-2)

在垃圾收集语言中处理非托管资源是不确定的。即使您明确地调用Dispose,您也无法控制何时实际释放后备内存。当对象超出范围时,无论是通过退出using语句,还是从下级方法弹出callstack,都会隐式调用Dispose。总而言之,有时该对象实际上可能是托管资源(例如文件)的包装器。这就是为什么在finally语句中显式关闭或使用using语句的好习惯。 干杯

答案 11 :(得分:-3)

MemorySteram只是一个字节数组,它是一个托管对象。 忘记处理或关闭除了最终确定之外没有副作用 只需在反射器中检查MemoryStream的constuctor或flush方法,就可以清楚为什么你不需要担心关闭或处理它,只是为了遵循良好的做法。