【问题标题】:OutOfMemoryException on MemoryStream writingMemoryStream 写入时出现 OutOfMemoryException
【发布时间】:2009-11-19 22:39:24
【问题描述】:

我有一个小示例应用程序,我正在尝试让一些新的 .Net 4.0 Parallel Extensions 运行(它们非常好)。我遇到了 OutOfMemoryException 的(可能非常愚蠢)问题。我希望将此示例插入的主应用程序读取一些数据和大量文件,对它们进行一些处理,然后将它们写出某个地方。我遇到了一些文件变大(可能是 GB)的问题,并且担心内存问题,所以我想并行化导致我走上这条路的事情。

现在下面的代码在较小的文件上获得了 OOME,我想我只是遗漏了一些东西。它将读取 10-15 个文件并很好地并行写入它们,但随后它会在下一个文件中窒息。看起来它的读写大约有 650MB。第二双眼睛将不胜感激。

我正在从 FileStream 读取 MemorySteam,因为这是主应用程序所需要的,我只是想在某种程度上复制它。它从所有类型的地方读取数据和文件,并将其作为 MemoryStreams 处理。

这是使用 .Net 4.0 Beta 2,VS 2010。

namespace ParellelJob
{
class Program
{
    BlockingCollection<FileHolder> serviceToSolutionShare;
    static void Main(string[] args)
    {
        Program p = new Program();
        p.serviceToSolutionShare = new BlockingCollection<FileHolder>();
        ServiceStage svc = new ServiceStage(ref p.serviceToSolutionShare);
        SolutionStage sol = new SolutionStage(ref p.serviceToSolutionShare);

        var svcTask = Task.Factory.StartNew(() => svc.Execute());
        var solTask = Task.Factory.StartNew(() => sol.Execute());

        while (!solTask.IsCompleted)
        {

        }

    }
}

class ServiceStage
{
    BlockingCollection<FileHolder> outputCollection;
    public ServiceStage(ref BlockingCollection<FileHolder> output)
    {
        outputCollection = output;
    }

    public void Execute()
    {
        var di = new DirectoryInfo(@"C:\temp\testfiles");
        var files = di.GetFiles();
        foreach (FileInfo fi in files)
        {
            using (var fs = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read))
            {
                int b;
                var ms = new MemoryStream();
                while ((b = fs.ReadByte()) != -1)
                {
                    ms.WriteByte((byte)b); //OutOfMemoryException Occurs Here
                }
                var f = new FileHolder();
                f.filename = fi.Name;
                f.contents = ms;

                outputCollection.TryAdd(f);
            }
        }
        outputCollection.CompleteAdding();

    }
}

class SolutionStage
{
    BlockingCollection<FileHolder> inputCollection;
    public SolutionStage(ref BlockingCollection<FileHolder> input)
    {
        inputCollection = input;
    }
    public void Execute()
    {
        FileHolder current;
        while (!inputCollection.IsCompleted)
        {
            if (inputCollection.TryTake(out current))
            {
                using (var fs = new FileStream(String.Format(@"c:\temp\parellel\{0}", current.filename), FileMode.OpenOrCreate, FileAccess.Write))
                {
                    using (MemoryStream ms = (MemoryStream)current.contents)
                    {
                        ms.WriteTo(fs);
                        current.contents.Close();
                    }
                }
            }
        }
    }
}

class FileHolder
{
    public string filename { get; set; }
    public Stream contents { get; set; }
}
}

【问题讨论】:

    标签: c# stream parallel-processing


    【解决方案1】:

    主逻辑看起来没问题,但是如果 main 中的空 while 循环是字面的,那么您正在消耗不必要的 CPU 周期。最好改用 solTask​​.Wait()。

    但是如果单个文件可以以千兆字节为单位运行,那么您仍然会遇到至少 1 个完全保存在内存中的问题,通常是 2 个(1 个正在读取,1 个正在处理/写入。

    PS1:我刚刚意识到您没有预先分配 MemStream。这很糟糕,它必须经常为大文件重新调整大小,这会占用大量内存。最好使用类似的东西:

    var ms = new MemoryStream(fs.Length);
    

    然后,对于大文件,您必须考虑大对象堆 (LOH)。您确定不能将文件分段并进行处理吗?

    PS2:你不需要构造函数参数上的 ref,但这不是问题。

    【讨论】:

    • 预分配 MemoryStream 解决了我的问题,但最大的文件除外。我将把较大的文件分成块。只是在这个示例应用程序中没有。谢谢。
    【解决方案2】:

    只是快速浏览一下,在你的 ServiceStage.Execute 方法中

    var ms = new MemoryStream();
    

    我看不到您在哪里关闭 ms 或使用它。您确实在其他课程中使用过。这是要检查的一件事。

    【讨论】:

    • 我试图通过将 MemoryStream 放在共享的 BlockingCollection 中来在类之间共享 MemoryStream。在 SolutionStage 完成后,流将被关闭。
    • 不,MemStreams 用于传输数据,在接收端关闭。并且完全被管理,你真的不需要 Dispose() 他们。
    猜你喜欢
    • 1970-01-01
    • 2013-01-28
    • 1970-01-01
    • 2012-01-03
    • 2012-10-05
    • 2020-02-10
    • 1970-01-01
    • 1970-01-01
    • 2020-02-20
    相关资源
    最近更新 更多