c# - 内存流和大对象堆

标签 c# wcf memory out-of-memory large-object-heap

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

因为我希望能够恢复文件并且我不想被 WCF 限制在我的文件大小上,所以我将文件分 block 为 1MB 的片段。这些“ block ”作为流传输。到目前为止效果很好。

我的步骤是:

  1. 打开文件流
  2. 从文件中读取 block 到字节[]并创建内存流
  3. 传输 block
  4. 回到 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/

相关文章:

memory - 在现有的大量代码中使用 jemalloc

带有适配器回收单元的android gridview header 解决方案

c# - UDP "Connect"- C# 中的速度

c# - 错误: Assembly uses System. Web.Mvc,引用程序集的版本较高

c# - 创建公共(public)属性时使用私有(private)变量的目的是什么?

c# - 如何在 C# 中将类型为 `object` 的变量转换为具有泛型类型的类?

web-services - 接收数据大小的 DynamicProxyFactory 错误

android - 如何使用 Eclipse Indigo 在 Android 中导入 Web 服务引用?

Java 客户端使用的本地主机上的 C# WCF Web 服务

java - 关闭 JavaFX 选项卡不会释放内存