从方法中返回内存流

6

想知道以下情况是否会导致内存泄漏。

Aspx页面包含以下内容。

private void Generator(input)
{
    using (MemoryStream memoryStream = Helper.Instance.Generate(input))
    {
    }
}

以下方法是从aspx页面调用的,它返回内存流。

MemoryStream Generate(input)
{
    MemoryStream stream = new MemoryStream();
    //doing some stream manipulation here

    return stream;
}

1
请记得给答案投票并接受它们。 - Ignacio Soler Garcia
1
我质疑释放MemoryStream的必要性。MemoryStreams会泄漏吗?它们不像由任何非托管资源支持的流那样。这只是从Stream继承IDisposable的产物。 - spender
4个回答

10
  • 第一点:如果代码在以下位置抛出异常:

    // doing some stream manipulation here
    
    如果Helper.Instance.Generate不返回MemoryStream,那么调用者也就不需要对其进行释放。第二点是,MemoryStream并不使用任何非托管资源,因此调用Dispose并不是必须的。因此,在这种情况下不会出现内存泄漏。如果在Helper.Instance.Generate中抛出异常,强制执行Dispose可能会更好:
    MemoryStream Generate(input)  
    {      
        MemoryStream stream = new MemoryStream();      
    
        try
        {
            //doing some stream manipulation here        
    
            return stream;  
        }
        catch
        {
            stream.Dispose();
            throw;
        }
    }  
    

    这是一种用于构造、操作和返回一个 IDisposable 对象的通用模式。


如果实现了Dispose,你应该始终调用它——安全第一。 - Random Dev
@CarstenKönig,始终处理IDisposable对象是一个很好的通用规则,但如果代码未处理已知不包装非托管资源(如MemoryStreamDataTableDataSet)的IDisposable对象,我也不会感到惊讶。 - Joe
只要“失败”意味着“什么都不做”,我通常不在意 - 而像“抛出异常”那样的“失败”,我还没有见过。 - Random Dev

0

这是完全有效的,不会生成内存泄漏,因为Dispose方法在退出using块后被调用。

与执行以下操作完全相同:

 private void Generator(input)
 {
    using (MemoryStream memoryStream = new MemoryStream())
    {
        Helper.Instance.Manipulate(memoryStream);
    }
 }

-1 - 这不是完全相同的:在原始版本中,如果操作抛出异常,没有返回需要处理的流。 - Joe

0

我认为即使是像MemoryStreamDataTable这样的东西,始终进行处理也有价值。这使得阅读代码并验证其正确性变得更加容易。如果您在using块中看到一个资源,那么您就知道它是正确的。无需在脑海中进行思维流追踪和模拟执行。

代码可以立即验证是否正确。毫不费力。我认为这很好。

话虽如此,虽然我尝试将甚至不必要的东西放入using块中,但我不会过度使用。如果有理由不处理它(即使是开发人员方便),我就不会处理。


仅因为一个可丢弃的资源在 "using" 块中,并不意味着它是正确的。它几乎肯定会被处理,但不对其进行处理只是使其不正确的其中一种方式。过早处理也是另一个潜在的问题。 - Servy
@Servy 在测试期间早期释放很容易被检测到。这是一个无害(低成本)的错误。而从未释放则可能不会被发现,这是危险的(代价高昂)。 - usr
在仍需要使用对象的情况下进行处理很可能会导致程序崩溃(如果该对象在被处理时确实执行了某些操作,这种情况就更为严重),或者至少不会按照预期运行。而如果应该进行处理而没有这样做,则会浪费系统资源(如果您的系统没有负载,那么后果可能很小)。无论哪种情况更糟糕,显然都是错误的。正确的方法是每次使用完毕后立即处理对象。 - Servy
当然。这就是使用的目的。它默认是正确的,因为变量在被处理时超出范围并且总是被处理掉;我想说的是:如果资源在using块(RAII风格)中,它永远不会泄漏。能够做出这样的声明是有价值的。 - usr
首先,using块并不能保证对象被处理。有几种异常情况会导致它没有被处理。其次,它假设创建的IDisposable对象从未需要超出它最初分配给的局部变量的生命周期。这通常是正确的,但并非总是如此。 - Servy

0

@Joe已经提供了一些解释。对于实现IDisposable接口的对象,您可以尽早调用Dispose来回收资源(托管和/或非托管),但您也可以决定不调用,因为CLR仍然跟踪它们,并通过终结器回收资源。通过终结器释放资源需要更长的时间,但可以防止内存泄漏。

只有当实现对象/类的人未能遵循设计准则并忘记正确编写终结器时,才会发生内存泄漏。

有大量的文章和书籍详细介绍了这些细节,例如

http://msdn.microsoft.com/en-us/magazine/cc163392.aspx

http://www.bluebytesoftware.com/blog/PermaLink.aspx?guid=88e62cdf-5919-4ac7-bc33-20c06ae539ae


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接