【问题标题】:Out of Memory Exception when handling large files in C#在 C# 中处理大文件时出现内存不足异常
【发布时间】:2014-01-27 09:25:47
【问题描述】:

我有一个 C# winforms 应用程序,其中我使用 OpenFileDialog 让用户选择要打开的文本文件。(允许多选) 一旦他们选择了文本文件,我就会一个一个地打开文件,获取文本并将内容存储在 List 变量中,方法是使用 List.Add() 操作。

当用户选择异常大量的文本文件时出现我的问题,例如 1264 个文本文件,总大小高达 750MB,程序无法处理。它最多读取大约 850 个文件,然后给我一个内存不足的异常。 在任务管理器中,发生这种情况时,我的应用程序的内存(私有工作集)约为 1.5GB。 我使用具有 32GB 内存的 x64 机器。

我给出了读取文件的代码:

public static List<LoadData> LoadDataFromFile(string[] filenames)
{
    List<LoadData> MasterData = new List<LoadData>();
    lookingForJobs = new LookingForJobs(1,filenames.Length);
    lookingForJobs.Show();
    /*-------OUTER LOOP TO GO THROUGH ALL THE FILES-------*/
    for (int index = 0; index < filenames.Length; index++)
    {
        string path = filenames[index];
        /*----------INNER LOOP TO GO THROUGH THE CONTENTS OF EACH FILE------*/
        foreach (string line in File.ReadAllLines(path))
        {
            string[] columns = line.Split('\t');
            if (columns.Length == 9)
            {
                if (line.StartsWith("<"))    /*-------IGNORING THE FIRST 8 LINES OF EACH LOG FILE CONTAINING THE LOGGER INFO---------*/
                {
                    MasterData.Add(new LoadData
                    {
                        Event_Type = columns[0],
                        Timestamp = columns[1],
                        Log_Message = columns[2],
                        Category = columns[3],
                        User = columns[4],
                        Thread_ID = columns[5],
                        Error_Code = columns[6],
                        Application = columns[7],
                        Machine = columns[8]
                    });
                }
            }
        }
        lookingForJobs.SearchingForJobsProgress.PerformStep();
        /*--------END OF INNER LOOP--------*/
    }
    lookingForJobs.Dispose();
    /*-----------END OF OUTER LOOP-----*/
    return MasterData;
}

编辑: 我知道我可能应该重新设计我的代码,以便不会一次将所有文件读入对象。但是,我想知道列表对象或内存(私有工作集)的大小是否有任何限制。我在几篇文章中读到,有时当您达到 1.5-1.6 GB 时,就会出现这类问题。

【问题讨论】:

  • 您是在 x64 模式下构建应用程序吗?
  • @Guffa 添加内存直到它爆裂不是必要正确的“修复”这里,特别是当流式(而不是缓冲)方法如此微不足道时(见康拉德的回答)
  • 你确定你需要内存中的所有数据吗?
  • @MarcGravell:由于处理完每个文件后数据都会被丢弃,我无法想象它会产生如此大的差异。
  • @Simon,是的,也许我可以采用一种在处理后删除一些数据的方法。我的 MasterData 列表大小达到 ~2564543 个元素。这里有限制吗?

标签: c# .net winforms visual-studio-2010 list


【解决方案1】:

使用File.ReadLines 而不是File.ReadAllLines,因为第二个不必要地将所有文件加载到内存中,而您一次只需要一行。 MSDN says:

当您使用 ReadAllLines 时,您必须等待返回整个字符串数组才能访问该数组。因此,当您处理非常大的文件时,ReadLines 会更有效率。

这可能会给你带来很大的内存改进。

第二个想法是重新考虑是否真的需要将这么大的数据存储在内存中。也许您可以只存储每个文件的文件路径并根据需要读取它们。

【讨论】:

  • 有时,看似微不足道的更改却如此有效。
  • 是的,我改了,同样的问题出现了。单个文件不是很大(每个文件约 650KB)。
  • 因为这些数据在处理完每个文件后都会被丢弃,所以不会有太大的不同。
  • @KonradKokosa:必须为任一解决方案创建所有字符串对象,因此与内存管理视图没有太大区别。
【解决方案2】:

您的文件可能很小,但您正在构建的 MasterData 对象仍会将其找到的所有数据存储在内存中...

听起来您可能需要重新设计一些应用程序...

您是否通过探查器运行过这个 - 看看您是否可以在整个程序过程中检查内存使用情况?

【讨论】:

  • 至少有两种可能:(1)使用x64(假设单个元素的数量不超过2^31)或(2)将MasterData数据写入文件,并进行处理以后一次记录。
【解决方案3】:

您正在创建的对象包含您从文件中读取的大部分数据。我认为类别、用户、错误代码、应用程序和机器的值会重复很多记录。

您可以制作一个包含这些字符串值的字典。对于您读取的每个值,您将检查字典中是否已经存在相等的字符串并使用它,否则添加它。这样这些字符串只会在内存中存在一次。

字符串实习使用相同的原则,但你实习的字符串将保留在内存中,直到应用程序关闭。通过将它们放入字典中,您可以在不再需要它们时将它们删除。

示例;制作字典:

Dictionary<string, string> values = new Dictionary<string, string>();

获取一个值并在字典中使用/注册一个值:

string category = columns[3];
if (values.ContainsKey(category)) {
  category = values[category];
} else {
  values.Add(category, category);
}

您当然只会在您希望有很多重复值的值上使用它。

【讨论】:

  • 这是一个我从未想过的有趣优化。如果实施将大大减少内存。谢谢!
猜你喜欢
  • 2016-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-04
  • 2011-09-16
  • 2020-08-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多