记忆流和大对象堆

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

因为我希望能够恢复文件并且我不希望受到WCF的文件大小限制,所以我将这些文件分块为1MB。 这些“块”以流的forms传输。 到目前为止哪个方面效果很好。

我的步骤是:

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

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

现在,我不想在消息中发送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); } } 

我希望这没关系。 这是我在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); 

这应该删除大量的分配。 如果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); } } 

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

我希望这有帮助

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

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

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

我对你问题的第一部分不太确定,但是对于更好的方法 – 你有没有考虑过BITS ? 它允许通过http下载文件的后台。 您可以提供http://或file:// URI。 它可以从中断的角度恢复,并使用http HEADER中的RANGE方法以字节块的forms下载。 它由Windows Update使用。您可以订阅提供有关进度和完成信息的事件。

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

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

我们的想法是创建一个临时文件(您不需要特定的权限来执行此操作),然后使用它类似于内存流。 使类Disposable将在使用后清除临时文件。

 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 

  // 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. } }