C#MemoryStream泄漏内存后,处理/关闭/ etc?

时间:2012-02-19 00:12:45

标签: c# .net memory

我一直在跟踪我的应用程序中的大量内存泄露,似乎问题是MemoryStream类。每当我使用一个,使用'using'关键字或显式关闭/ dispose时,垃圾收集器永远不会收集内存。这有什么不对?

byte[] bData = System.IO.File.ReadAllBytes( "F:\\application_exit_bw.png" );
using( System.IO.MemoryStream hMemoryStreamOutput = new System.IO.MemoryStream())
{
    for ( int i = 0; i < 10000; i++ ) hMemoryStreamOutput.Write( bData, 0, bData.Length );
}
Thread.Sleep(Timeout.Infinite);

使用显式关闭/ dipose,行为保持不变。内存被占用并将保持这种状态,直到我关闭我的应用程序,或者,应用程序填满了所有系统内存。帮助

5 个答案:

答案 0 :(得分:7)

MemoryStream类或示例代码中的用法没有任何问题。不再有.Net中的GC不会立即清理内存。相反,当堆中的可用空间达到某个阈值或通过GC.Collect调用显式调用时,它会回收它。

在这种情况下,释放内存的唯一方法是在using语句之后和Thread.Sleep调用之前立即发生GC。这是不太可能发生的,因此如果您对程序进行了分析,当它实际上没有泄漏时会出现内存泄漏

答案 1 :(得分:5)

这是非确定性GC的症状。 GC不会对任何保证何时释放内存。这是正常的,预期的和想要的行为。

尝试调用GC.Collect()以查看是否可以解决您的问题。此外,您需要在发布模式下运行,因为在调试模式下,JIT会将局部变量的生命周期延长到方法的末尾,即使它们在某个点之后未被使用也是如此。

答案 2 :(得分:1)

问题的另一方面是您正在使用什么来确定“内存泄漏”。有许多不同的方法来衡量“免费”记忆,依赖于它,你可能得到完全不同的结果。

  • 内存使用情况显示在任务管理器中 - 即使由于GC使用内存的方式CLR GC认为所有内存都“空闲”,也不会发生故障。
  • GC内存性能计数器(和属性) - 这些实际上将显示GC在内存上的视图。您希望使用它们来检测托管内存泄漏。

MemoryStream(以及任何其他大型86K +)分配还有一件事 - 它们使用仅在完整GC上收集的大对象堆,触发它可能需要运行GC.Collect两次。在应用程序的正常流程中,它将足够重复,因此您可能无法在应用程序关闭之前释放此内存。诊断 - 检查GC收集性能计数器(GC的数量)。

还有一个:如果你正在尝试解决内存泄漏,因为你遇到“内存不足”异常,可能是由地址空间碎片引起的(通常只有32位进程)。如果是这种情况 - 考虑创建自己的内存流,不在单个块中分配内存,然后在增长流时必须复制它。或者至少尝试预先分配流中的空间。

答案 3 :(得分:0)

我已将此方法用于批处理

    static byte[] buffer;

    public static object Read(XmlDocument xmlDocument)
    {
        if (buffer == null)
        {
            buffer = new byte[1024 * 1024 * 512];
        }

        if (xmlDocument != null)
        {
            using (MemoryStream ms = new MemoryStream(buffer))
            {
                xmlDocument.Save(ms);
                ms.Flush();
                ms.Seek(0, SeekOrigin.Begin);

                object  result = ReadFromStream(ms);

                ms.Close();
                return result;
            }
        }
        else
        {
            return null;
        }
    }

答案 4 :(得分:0)

调用GC.Collect()不是一个好习惯,不应该用作解决方案。

您可以尝试查看是否有任何改变,但不依赖于故意GC.Collect()调用......