内存流和大对象堆

fla*_*ayn 14 c# memory wcf out-of-memory large-object-heap

我必须使用WCF通过不可靠的连接在计算机之间传输大文件.

因为我希望能够恢复该文件,并且我不希望受到WCF的文件大小限制,我将这些文件分块为1MB.这些"块"以流的形式传输.到目前为止哪个效果很好.

我的步骤是:

  1. 打开文件流
  2. 从文件中读取块到byte []并创建内存流
  3. 转移块
  4. 返回2.直到整个文件发送

我的问题在第2步.我假设当我从字节数组创建一个内存流时,它将最终在LOH上并最终导致outofmemory异常.我实际上无法创建此错误,也许我的假设是错误的.

现在,我不想在消息中发送byte [],因为WCF会告诉我数组大小太大.我可以更改允许的最大数组大小和/或我的块的大小,但我希望有另一种解决方案.

我的实际问题:

  • 我当前的解决方案是否会在LOH上创建对象并且会导致我的问题吗?
  • 有没有更好的方法来解决这个问题?

顺便说一句:在接收端,我简单地从到达流中读取较小的块并将它们直接写入文件,因此不涉及大字节数组.

编辑:

当前解决方案

for (int i = resumeChunk; i < chunks; i++)
{
 byte[] buffer = new byte[chunkSize];
 fileStream.Position = i * chunkSize;
 int actualLength = fileStream.Read(buffer, 0, (int)chunkSize);
 Array.Resize(ref buffer, actualLength);
 using (MemoryStream stream = new MemoryStream(buffer)) 
 {
  UploadFile(stream);
 }
}
Run Code Online (Sandbox Code Playgroud)

Joe*_*nds 35

我希望这没关系.这是我在StackOverflow上的第一个答案.

是绝对的,如果你的chunksize超过85000字节,那么数组将在大对象堆上分配.您可能不会很快耗尽内存,因为您正在分配和释放大小相同的连续内存区域,因此当内存填满运行时可以将新块装入旧的回收内存区域.

我会有点担心Array.Resize调用,因为这将创建另一个数组(请参阅http://msdn.microsoft.com/en-us/library/1ffy6686(VS.80).aspx).如果actualLength == Chunksize,这是一个不必要的步骤,因为除了最后一个块之外,它将是所有的.所以我至少建议:

if (actualLength != chunkSize) Array.Resize(ref buffer, actualLength);
Run Code Online (Sandbox Code Playgroud)

这应该删除大量的分配.如果actualSize与chunkSize不同但仍然> 85000,则新数组也将在Large对象堆上分配,可能导致它碎片并可能导致明显的内存泄漏.我相信它仍然需要很长时间才能实际耗尽内存,因为泄漏会很慢.

我认为更好的实现方法是使用某种缓冲池来提供数组.你可以自己动手(这太复杂了)但是WCF确实为你提供了一个.我稍微重写了你的代码,以便采取以下措施:

BufferManager bm = BufferManager.CreateBufferManager(chunkSize * 10, chunkSize);

for (int i = resumeChunk; i < chunks; i++)
{
    byte[] buffer = bm.TakeBuffer(chunkSize);
    try
    {
        fileStream.Position = i * chunkSize;
        int actualLength = fileStream.Read(buffer, 0, (int)chunkSize);
        if (actualLength == 0) break;
        //Array.Resize(ref buffer, actualLength);
        using (MemoryStream stream = new MemoryStream(buffer))
        {
            UploadFile(stream, actualLength);
        }
    }
    finally
    {
        bm.ReturnBuffer(buffer);
    }
}
Run Code Online (Sandbox Code Playgroud)

这假设可以重写UploadFile的实现以获取no的int.要写的字节数.

我希望这有帮助


Man*_*gor 5

另请参见RecyclableMemoryStream.从这篇文章:

Microsoft.IO.RecyclableMemoryStream是一个MemoryStream替代品,可为性能关键型系统提供卓越的性能.特别是它被优化以执行以下操作:

  • 使用池化缓冲区消除大对象堆分配
  • 导致2代GC的数量要少得多,并且由于GC而停顿的时间要少得多
  • 通过限制池大小避免内存泄漏
  • 避免内存碎片
  • 提供出色的可调试性
  • 提供绩效跟踪指标