如果.NET中的MemoryStream没有关闭,是否会造成内存泄漏?

发布于 2024-07-08 01:16:47 字数 320 浏览 8 评论 0原文

我有以下代码:

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

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

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

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

I have the following code:

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

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

Is there any chance that the MemoryStream that I've allocated will somehow fail to be disposed of later?

I've got a peer review insisting that I manually close this, and I can't find the information to tell if he has a valid point or not.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

天涯沦落人 2024-07-15 01:16:48

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

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

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

Calling .Dispose() (or wrapping with Using) is not required.

The reason you call .Dispose() is to release the resource as soon as possible.

Think in terms of, say, the Stack Overflow server, where we have a limited set of memory and thousands of requests coming in. We don't want to wait around for scheduled garbage collection, we want to release that memory ASAP so it's available for new incoming requests.

月光色 2024-07-15 01:16:48

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

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;
    }
}

This is already answered, but I'll just add that the good old-fashioned principle of information hiding means you may at some future point want to refactor:

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

to:

Stream foo()
{    
   ...
}

This emphasizes that callers should not care what kind of Stream is being returned, and makes it possible to change the internal implementation (e.g. when mocking for unit testing).

You then will need be in trouble if you haven't used Dispose in your bar implementation:

void bar()
{    
    using (Stream s = foo())
    {
        // do stuff with s
        return;
    }
}
刘备忘录 2024-07-15 01:16:48

所有流都实现 IDisposable。 将您的内存流包装在 using 语句中,您就会变得很好。 无论如何,using 块将确保您的流被关闭和处置。

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

All streams implement IDisposable. Wrap your Memory stream in a using statement and you'll be fine and dandy. The using block will ensure your stream is closed and disposed no matter what.

wherever you call Foo you can do using(MemoryStream ms = foo()) and i think you should still be ok.

虚拟世界 2024-07-15 01:16:48

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

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

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

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;
}

I would recommend wrapping the MemoryStream in bar() in a using statement mainly for consistency:

  • Right now MemoryStream does not free memory on .Dispose(), but it is possible that at some point in the future it might, or you (or someone else at your company) might replace it with your own custom MemoryStream that does, etc.
  • It helps to establish a pattern in your project to ensure all Streams get disposed -- the line is more firmly drawn by saying "all Streams must be disposed" instead of "some Streams must be disposed, but certain ones don't have to"...
  • If you ever change the code to allow for returning other types of Streams, you'll need to change it to dispose anyway.

Another thing I usually do in cases like foo() when creating and returning an IDisposable is to ensure that any failure between constructing the object and the return is caught by an exception, disposes the object, and rethrows the 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;
}
未央 2024-07-15 01:16:48

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

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

You won't leak memory, but your code reviewer is correct to indicate you should close your stream. It's polite to do so.

The only situation in which you might leak memory is when you accidentally leave a reference to the stream and never close it. You still aren't really leaking memory, but you are needlessly extending the amount of time that you claim to be using it.

紫竹語嫣☆ 2024-07-15 01:16:48

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

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

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

If an object implements IDisposable, you must call the .Dispose method when you're done.

In some objects, Dispose means the same as Close and vice versa, in that case, either is good.

Now, for your particular question, no, you will not leak memory.

自由如风 2024-07-15 01:16:48

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

I'm no .net expert, but perhaps the problem here is resources, namely the file handle, and not memory. I guess the garbage collector will eventually free the stream, and close the handle, but I think it would always be best practice to close it explicitly, to make sure you flush out the contents to disk.

慢慢从新开始 2024-07-15 01:16:48

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

Disposal of unmanaged resources is non-deterministic in garbage collected languages. Even if you call Dispose explicitly, you have absolutely no control over when the backing memory is actually freed. Dispose is implicitly called when an object goes out of scope, whether it be by exiting a using statement, or popping up the callstack from a subordinate method. This all being said, sometimes the object may actually be a wrapper for a managed resource (e.g. file). This is why it's good practice to explicitly close in finally statements or to use the using statement.
Cheers

吃兔兔 2024-07-15 01:16:48

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

MemorySteram is nothing but array of byte, which is managed object.
Forget to dispose or close this has no side effect other than over head of finalization.
Just check constuctor or flush method of MemoryStream in reflector and it will be clear why you don't need to worry about closing or disposing it other than just for matter of following good practice.

深府石板幽径 2024-07-15 01:16:47

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

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

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

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

You won't leak anything - at least in the current implementation.

Calling Dispose won't clean up the memory used by MemoryStream any faster. It will stop your stream from being viable for Read/Write calls after the call, which may or may not be useful to you.

If you're absolutely sure that you never want to move from a MemoryStream to another kind of stream, it's not going to do you any harm to not call Dispose. However, it's generally good practice partly because if you ever do change to use a different Stream, you don't want to get bitten by a hard-to-find bug because you chose the easy way out early on. (On the other hand, there's the YAGNI argument...)

The other reason to do it anyway is that a new implementation may introduce resources which would be freed on Dispose.

暮凉 2024-07-15 01:16:47

如果某物是一次性的,您应该始终丢弃它。 您应该在 bar() 方法中使用 using 语句来确保 ms2 得到 Dispose。

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

If something is Disposable, you should always Dispose it. You should be using a using statement in your bar() method to make sure ms2 gets Disposed.

It will eventually get cleaned up by the garbage collector, but it is always good practice to call Dispose. If you run FxCop on your code, it would flag it as a warning.

驱逐舰岛风号 2024-07-15 01:16:47

是的,存在泄漏,具体取决于您如何定义泄漏以及您的意思是“稍后”......

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

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

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

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

Yes there's a leak, depending on how you define LEAK and how much LATER you mean...

If by leak you mean "the memory remains allocated, unavailable for use, even though you're done using it" and by latter you mean anytime after calling dispose, then then yes there may be a leak, although its not permanent (i.e. for the life of your applications runtime).

To free the managed memory used by the MemoryStream, you need to unreference it, by nullifying your reference to it, so it becomes eligible for garbage collection right away. If you fail to do this, then you create a temporary leak from the time you're done using it, until your reference goes out of scope, because in the meantime the memory will not be available for allocation.

The benefit of the using statement (over simply calling dispose) is that you can DECLARE your reference in the using statement. When the using statement finishes, not only is dispose called, but your reference goes out of scope, effectively nullifying the reference and making your object eligible for garbage collection immediately without requiring you to remember to write the "reference=null" code.

While failing to unreference something right away is not a classical "permanent" memory leak, it definitely has the same effect. For example, if you keep your reference to the MemoryStream (even after calling dispose), and a little further down in your method you try to allocate more memory... the memory in use by your still-referenced memory stream will not be available to you until you nullify the reference or it goes out of scope, even though you called dispose and are done using it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文