如果.NET中的MemoryStream未closures,是否会产生内存泄漏?
我有以下代码:
MemoryStream foo(){ MemoryStream ms = new MemoryStream(); // write stuff to ms return ms; } void bar(){ MemoryStream ms2 = foo(); // do stuff with ms2 return; }
有没有机会,我已经分配的MemoryStream将以某种方式不能被丢弃?
我有一个同行评审坚持我手动closures这个,我找不到信息来判断他是否有一个有效的点。
如果某件事是一次性的,你应该总是处置它。 你应该在你的bar()方法中使用using语句来确保ms2被取消。
它最终会被垃圾收集器清理干净,但Dispose始终是一个好习惯。 如果你在你的代码上运行FxCop,它会将其标记为警告。
至less在目前的实施中,你不会泄漏任何东西。
调用Dispose不会更快地清理MemoryStream使用的内存。 在通话之后, 它将阻止您的信息stream对于读/写通话可行,这对您可能有用或不可用。
如果你完全确定你永远不想从MemoryStream移动到另一种types的stream,那么对于不调用Dispose不会有任何伤害。 但是,这通常是一个好的做法,部分原因是因为如果你改变了使用不同的Stream,你不想被一个难以发现的错误所困扰,因为你早期select了简单的方法。 (另一方面,有YAGNI的说法…)
无论如何,做这件事的另一个原因是,一个新的实现可能会引入Dispose上释放的资源。
是的,有一个泄漏 ,这取决于你如何定义泄漏和多less你后来的意思是…
如果通过泄漏,你的意思是“内存仍然分配,即使你已经完成了使用,也不能使用”。后者是指在调用dispose之后的任何时候,然后是可能会有泄漏,虽然它不是永久性的应用程序运行时的生命周期)。
为了释放MemoryStream使用的托pipe内存, 您需要将其取消引用 ,通过取消对引用的引用,以便它立即可以进行垃圾回收。 如果你不这样做,那么你创build一个临时的泄漏,直到你的引用超出了范围,因为在这期间内存将不能被分配。
使用语句的好处(通过简单地调用dispose)是,您可以在using语句中声明您的引用。 当使用语句结束时,不仅可以调用dispose,而且可以引用超出范围,从而有效地取消引用并使对象立即符合垃圾回收的条件,而不要求您记住写入“reference = null”代码。
虽然没有马上解决这个问题并不是一个经典的“永久性”的内存泄漏,但它确实具有相同的效果。 例如,如果您保留对MemoryStream的引用(即使在调用dispose之后),并且在方法中更深入一点,也可以尝试分配更多的内存…由您仍然引用的内存stream使用的内存将不可用直到你取消了引用,或者它超出了范围,即使你调用了dispose并且使用了它。
这已经回答了,但我只是补充说,信息隐藏的好老式的原则意味着你可能在未来某个时候想要重构:
MemoryStream foo() { MemoryStream ms = new MemoryStream(); // write stuff to ms return ms; }
至:
Stream foo() { ... }
这强调了调用者不应该关心返回什么types的Stream,并且可以改变内部实现(例如,当嘲笑unit testing时)。
如果你没有在你的bar实现中使用Dispose,那么你将会遇到麻烦:
void bar() { using (Stream s = foo()) { // do stuff with s return; } }
所有的stream都实现IDisposable。 用using语句包装你的Memorystream,你会很好,很好看。 使用块将确保您的stream是closures和处置无论如何。
无论你在哪里调用Foo,你都可以使用(MemoryStream ms = foo()),我认为你应该还可以。
调用.Dispose()
(或Using
包装”)不是必需的。
您调用.Dispose()
的原因是尽快释放资源 。
比如说Stack Overflow服务器,我们有一组有限的内存和数千个请求。我们不想等待计划的垃圾收集,我们希望尽快释放内存,以便可用为新的传入的请求。
你不会泄漏内存,但你的代码审查是正确的,表明你应该closures你的stream。 这样做很有礼貌
您可能会泄漏内存的唯一情况是您不小心留下了对stream的引用,并且从不closures它。 你仍然没有真正的内存泄漏,但是你不必要地延长了你声称使用它的时间。
我build议在using
语句的bar()
中包装MemoryStream,主要是为了保持一致性:
- 现在MemoryStream不会释放
.Dispose()
上的内存,但是有可能在将来的某个时刻,或者你(或者你公司的其他人)可能用你自己定制的MemoryStream代替它。 - 这有助于在你的项目中build立一个模式,以确保所有的 Streams被处置 – 通过说“所有的Streams必须被处理”而不是“一些Streams必须被处理,但是某些不必处理” …
- 如果您更改了代码以允许返回其他types的Stream,则需要将其更改为无论如何处置。
在创build和返回一个IDisposable时,我通常在做例如foo()
时做的另一件事是确保构造对象和return
之间的任何失败都被exception捕获,处理对象并抛出exception:
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; }
如果一个对象实现了IDisposable,那么当你完成时你必须调用.Dispose方法。
在一些对象中,Dispose的意思与Close相同,反之亦然,在这种情况下,要么是好的。
现在,对于你的特定问题,不,你不会泄漏记忆。
我不是.net专家,但也许这里的问题是资源,即文件句柄,而不是内存。 我猜垃圾收集器最终会释放这个stream,然后closures这个句柄,但是我认为最好的做法是明确地closures它,以确保将内容清空到磁盘。
垃圾收集语言中非托pipe资源的处理是非确定性的。 即使您明确调用Dispose,也完全无法控制后备内存何时被释放。 当对象超出范围时,Dispose被隐式调用,无论是通过退出using语句,还是从下级方法popup调用堆栈。 这一切被说,有时对象可能实际上是托pipe资源(例如文件)的包装。 这就是为什么在最后的语句中显式closures或使用using语句是很好的做法。 干杯
MemorySteram不过是字节数组,它是被pipe理的对象。 忘记处理或closures这个没有任何副作用,除了头顶定案。
只需在reflection器中检查构造器或者刷新MemoryStream的方法,就可以清楚地知道为什么你不必担心closures或者处理它,而不仅仅是为了跟随好的练习。