我无法找到内存泄漏

时间:2013-05-05 14:30:22

标签: c# memory-leaks stream garbage-collection chunking

也许这不是一个好问题,但我有点绝望。我有一小段代码,我有内存泄漏,但我不知道如何克服它。请帮忙。

        var nPiece = stream.Length / BufferLen;
        var lastPieceLen = stream.Length - (nPiece * BufferLen);

        for (int i = 0; i < nPiece + 1; i++)
        {
            var buffer = new byte[i != nPiece ? BufferLen : lastPieceLen];
            stream.Read(buffer, 0, buffer.Length); 
            using (var chunk = new MemoryStream(buffer))
                SsClient.SendChunk(i != nPiece ? (i + 1).ToString() : ((i + 1) + "last"), SsSession, chunk);
            buffer = null;
            GC.Collect();
        }

我将一个大型流拆分为较小的块,并通过SsClient.SendChunk()方法将它们发送到WCF服务。让我们说我有一个700 MB的文件,我把它分成7块100 MB的块,并逐个发送。但是在这个方法完成后,内存大约有400 MB,我认为是内存泄漏。当我调试它时,我看到内存充满了网络服务的SendChunk方法后的块。当方法在这里完成时,我留下了内存泄漏。 GC.collect()似乎也不起作用。我甚至不明白为什么有400 MB的文件剩余400 MB?也许这可能会给我一些我不知道的线索。

任何想法?

1 个答案:

答案 0 :(得分:3)

    for (int i = 0; i < nPiece + 1; i++)
    {
         var buffer = new byte[i != nPiece ? BufferLen : lastPieceLen];

您没有内存泄漏。你只是得到了一个关于零食的坏情况。你匆忙地吞噬内存,在for()循环中分配缓冲区。它也不是微妙的,100兆的缓冲区不会从天而降。从大对象堆分配大于85,000字节的任何对象,而不是常规的分代GC堆。 LOH分配不会被压缩,它们太大,并且不会经常收集。它需要第2代第2集,它们并不经常发生。

如此分配会产生延迟效果,因此您没有看到GC.Collect()会减少数量。你也使用了很多内存。 Windows必须先取消映射内存页面。通常因为另一个进程需要RAM。而且你正在吞噬大量的虚拟内存地址空间。 Windows内存管理器也不急于取消分配该空间。哪个没关系,它是虚拟的。它不需要任何费用。否则你不清楚你在看什么号码。 GC.Collect()影响不大的最大原因是因为MemoryStream仍然有对数组的引用。调用它的Dispose()方法不会改变它。最后但同样重要的是,当您构建并运行Release版本时,抖动会删除对本地变量的null赋值。

一个简单的解决方法是重新使用缓冲区。在for()循环之外分配它。如果您还调整SendChunk()以获取长度参数,它将会有很大帮助。而简单地减少块大小,100兆就太多了。这在大多数WCF场景中通过网络进行,网络块大小通常仅为1500字节。在大多数情况下,I / O的缓冲区大小约为4096字节。