C# 内存流和大型对象堆

C# 内存流和大型对象堆,c#,wcf,memory,out-of-memory,large-object-heap,C#,Wcf,Memory,Out Of Memory,Large Object Heap,我必须使用WCF通过不可靠的连接在计算机之间传输大型文件 因为我希望能够恢复文件,并且我不希望WCF限制我的文件大小,所以我将文件分块成1MB的块。这些“块”以流的形式传输。到目前为止,效果相当不错 我的步骤是: 打开文件流 将区块从文件读入字节[],并创建memorystream 转移块 回到2。直到整个文件被发送 我的问题在第二步。我假设,当我从字节数组创建内存流时,它将结束在LOH上,并最终导致outofmemory异常。我实际上不能制造这个错误,也许我的假设是错误的 现在,我不想发送消息

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

因为我希望能够恢复文件,并且我不希望WCF限制我的文件大小,所以我将文件分块成1MB的块。这些“块”以流的形式传输。到目前为止,效果相当不错

我的步骤是:

  • 打开文件流
  • 将区块从文件读入字节[],并创建memorystream
  • 转移块
  • 回到2。直到整个文件被发送
  • 我的问题在第二步。我假设,当我从字节数组创建内存流时,它将结束在LOH上,并最终导致outofmemory异常。我实际上不能制造这个错误,也许我的假设是错误的

    现在,我不想发送消息中的字节[],因为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);
     }
    }
    
    for(int i=resumeChunk;i
    我对你问题的第一部分不太确定,但关于更好的方法,你考虑过了吗?它允许通过http在后台下载文件。您可以为它提供http://或file://URI。它可以从中断点恢复,并使用http头中的RANGE方法以字节块的形式下载。Windows Update使用它。您可以订阅提供进度和完成信息的事件

    我希望这没问题。这是我关于StackOverflow的第一个答案

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

    我会有点担心数组。调整调用大小,因为这将创建另一个数组(请参阅)。如果actualLength==Chunksize,这是一个不必要的步骤,除了最后一个块之外,其他所有块都是这样。因此,我至少建议:

    if (actualLength != chunkSize) Array.Resize(ref buffer, actualLength);
    
    这将删除大量的分配。如果actualSize与chunkSize不相同,但仍然>85000,则新数组也将分配到大型对象堆上,这可能会导致其碎片化,并可能导致明显的内存泄漏。我相信,由于泄漏速度相当缓慢,实际内存耗尽仍然需要很长时间

    我认为更好的实现是使用某种缓冲池来提供阵列。你可以自己滚动(这太复杂了),但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);
        }
    }
    
    BufferManager bm=BufferManager.CreateBufferManager(chunkSize*10,chunkSize);
    for(inti=resumeChunk;i
    这假设UploadFile的实现可以被重写,以整数表示要写入的字节数

    我希望这有帮助


    我想出了另一个解决办法,让我知道你的想法

    因为我不想在内存中存储大量数据,所以我一直在寻找一种优雅的方法来临时存储字节数组或流

    其思想是创建一个临时文件(您不需要特定的权限来执行此操作),然后将其与内存流类似地使用。使类成为一次性类将在临时文件使用后清理它

    public class TempFileStream : Stream
    {
      private readonly string _filename;
      private readonly FileStream _fileStream;
    
      public TempFileStream()
      {
         this._filename = Path.GetTempFileName();
         this._fileStream = File.Open(this._filename, FileMode.OpenOrCreate, FileAccess.ReadWrite);
      }
    
      public override bool CanRead
      {
       get
        {
        return this._fileStream.CanRead;
        }
       }
    
    // and so on with wrapping the stream to the underlying filestream
    

    另见。 发件人:

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

    • 通过使用池缓冲区消除大型对象堆分配
    • 产生的第2代地面军事系统少得多,由于地面军事系统暂停的时间少得多
    • 通过限制池大小来避免内存泄漏
    • 避免内存碎片
    • 提供出色的可调试性
    • 提供性能跟踪的指标

    谢谢你的建议,但我不想在每台机器上都安装IIS。没问题,只是想一想。我要指出的是,如果每台机器都在上传,那么您只需要在每台机器上安装IIS。如果客户端仅使用BITS下载,则不需要IIS。非常感谢!这真是一个高明的回答。感谢您指出阵列。调整大小问题。我也从未听说过BufferManager,听起来这在其他方面也会对我有很大帮助。这比我预期的要多得多,所以我想开始一个小的悬赏,并把它给你,但我必须在开始悬赏后等待23小时。。。所以你也必须等待:)谢谢你。我真的很高兴能帮上忙。如果还有别的事,请告诉我。回过头来看,也许值得指出的是,最佳的实现将共享一个singl
        // finally overrride the Dispose Method and remove the temp file     
    protected override void Dispose(bool disposing)
      {
          base.Dispose(disposing);
    
      if (disposing)
      {
       this._fileStream.Close();
       this._fileStream.Dispose();
    
       try
       {
          File.Delete(this._filename);
       }
       catch (Exception)
       {
         // if something goes wrong while deleting the temp file we can ignore it.
       }
      }