我必须使用 WCF 通过不可靠的连接在计算机之间传输大文件。
因为我希望能够恢复文件并且我不想被 WCF 限制在我的文件大小上,所以我将文件分 block 为 1MB 的片段。这些“ block ”作为流传输。到目前为止效果很好。
我的步骤是:
- 打开文件流
- 从文件中读取 block 到字节[]并创建内存流
- 传输 block
- 回到 2. 直到整个文件发送完毕
我的问题出在第 2 步。我假设当我从字节数组创建内存流时,它将最终出现在 LOH 上并最终导致内存不足异常。我实际上无法创建此错误,也许我的假设是错误的。
现在,我不想在消息中发送 byte[],因为 WCF 会告诉我数组大小太大。我可以更改允许的最大数组大小和/或我的 block 的大小,但我希望有另一种解决方案。
我的实际问题:
- 我当前的解决方案会在 LOH 上创建对象吗?这会给我带来问题吗?
- 有没有更好的方法来解决这个问题?
顺便说一句:在接收端,我简单地从到达的流中读取较小的 block 并将它们直接写入文件,因此不涉及大字节数组。
编辑:
目前的解决方案:
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 上的第一个答案。
是的,如果您的 block 大小超过 85000 字节,那么该数组将在大对象堆上分配。您可能不会很快耗尽内存,因为您正在分配和释放大小相同的连续内存区域,因此当内存填满时,运行时可以将新 block 放入旧的回收内存区域。
我会有点担心 Array.Resize 调用,因为它会创建另一个数组(参见 http://msdn.microsoft.com/en-us/library/1ffy6686(VS.80).aspx)。如果actualLength==Chunksize,这是一个不必要的步骤,因为它将适用于除最后一个 block 之外的所有 block 。所以我至少建议:
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);
}
}
这假设 UploadFile 的实现可以被重写为取一个 int 为 no。要写入的字节数。
希望对你有帮助
乔
关于c# - 内存流和大对象堆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2819081/