当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如果.NET中的MemoryStream未关闭,是否会创建内存泄漏?》经验,为你挑选了6个好方法。

我有以下代码:

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

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

我分配的MemoryStream是否有可能以后不能被处理掉?

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



1> Jon Skeet..:

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

调用Dispose不会更快地清理MemoryStream使用的内存.它被可行的通话,这可能会或可能不会对你有用后读/写调用停止您的流.

如果你完全确定你永远不想从MemoryStream转移到另一种流,那么不打算不调用Dispose会对你造成任何伤害.然而,这通常是一种很好的做法,部分原因是如果你做了改变以使用不同的Stream,你不希望被难以发现的bug所困扰,因为你很早就选择了简单的方法.(另一方面,有YAGNI的论点......)

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


YAGNI论证可以采取两种方式 - 因为决定不处理实现'IDisposable'的东西是一种违背正常最佳实践的特殊情况,你可能会认为这是你不应该做的事情,直到你真的需要,根据YAGNI原则.

2> Rob Prouse..:

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

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


@Grauenwolf:你的断言破坏了封装.作为一个消费者,你不应该关心它是否是无操作:如果它是IDisposable,那么Dispose()就是你的工作.
使用块调用dispose为您.
你不应该处理IDisposable对象的另一个例子是HttpClient https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/来自BCL的另一个例子,其中有IDisposable对象,你不需要(甚至不应该处置它.这只是要记住,通常在一般规则中有一些例外,即使在BCL中也是如此;)
对于StreamWriter类,情况并非如此:如果您处理StreamWriter,它将仅处理连接的流** - 如果它被垃圾收集并且其终结器被调用,它将永远不会处理流 - 这是设计的.
我知道这个问题是从2008年开始的,但今天我们有了.NET 4.0 Task库.在[大多数](http://stackoverflow.com/a/5986082)中[Dispos()是[不必要的](http://blogs.msdn.com/b/pfxteam/archive/2012/03/25/10287435.aspx)/138757)[案例](http://stackoverflow.com/a/3734298/138757)使用任务时.虽然我同意IDisposable _should_意思是"当你完成时你最好处理掉它",但这并不意味着它了.
有关信息,在一些简单的测试中(对于并行线程),FxCop和VSTS分析都没有发现一个微不足道的错过"使用"
@Phil再过几年.文章只讨论"任务"的情况,在大多数情况下,手动处理是不必要的.使用IO时,处理通常会更好,因为它可以使行为更具确定性.(例如,不处理`FileStream`可以长时间保持锁定,阻止其他应用程序或部分代码.)

3> Triynko..:

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

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

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

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

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


我喜欢这种回应.有时人们会忘记使用的双重职责:急切的资源回收*和*急切的解除引用.
垃圾收集器和抖动不会那样工作.范围是一种语言结构,而不是运行时将遵循的东西.实际上,通过在块结束时添加对.Dispose()的调用,可能会延长引用在内存中的时间.见http://ericlippert.com/2015/05/18/when-everything-you-know-is-wrong-part-one/

4> Joe..:

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

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

至:

Stream foo()
{    
   ...
}

这强调,主叫方不应该关心正在返回什么样的数据流,并有可能改变内部实现(如单元测试嘲笑时).

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

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



5> Jeff Atwood..:

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

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

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


在MemoryStream上调用Dispose不会释放任何内存.实际上,在调用Dispose之后,你仍然可以在MemoryStream中获取数据 - 试试:)
-1虽然对于MemoryStream来说是正确的,但作为一般建议,这是完全错误的.Dispose是释放*unmanaged*资源,例如文件句柄或数据库连接.记忆不属于那个类别.你几乎总是应该等待计划的垃圾收集来释放内存.
FileStream涉及*非托管*资源,实际上可以在调用Dispose时立即释放.另一方面,MemoryStream在其_buffer变量中存储*managed*字节数组,该变量在处理时不会被释放.实际上,_Stuffer甚至没有在MemoryStream的Dispose方法中被清空,这是一个SHAMEFUL BUG IMO,因为对引用进行清零可以使内存符合GC的处理时间.相反,延迟(但处置)的MemoryStream引用仍保留在内存中.因此,一旦处置它,如果它仍在范围内,您也应该将其置空.

6> Nick..:

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

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

推荐阅读
pan2502851807
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有