2014-01-27 3 views
2

У меня есть приложение winforms для C#, в котором я использую OpenFileDialogOpenFileDialog, чтобы пользователи могли выбирать текстовые файлы для открытия. (Разрешено множественное выделение) Как только они выбирают текстовые файлы, я откройте файлы один за другим, получите текст и сохраните содержимое в переменной List с помощью операции List.Add().Исключение из памяти Исключение при обработке больших файлов в C#

Моя проблема возникает, когда пользователь выбирает необычно большое количество текстовых файлов, таких как 1264 текстовых файлов общим размером до 750 МБ, программа не может его обработать. Он считывает до 850 файлов, а затем дает мне исключение из памяти. В диспетчере задач память моего приложения (частный рабочий набор) составляет около 1,5 ГБ, когда это происходит. Я использую машину x64 с 32 ГБ оперативной памяти.

Я даю код, который считывает через файлы:

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; 
} 

Edit: Я понимаю, что я должен возможно переделать мой код так, что не все файлы считываются в объект сразу. Но я хочу знать, есть ли ограничение на размер объекта списка или памяти (частный рабочий набор). Я читал в нескольких статьях, что когда вы попадаете в 1,5-1,6 ГБ, эти проблемы возникают.

+2

Вы строите приложение в режиме x64? – Guffa

+3

@ Guffa добавляет память до тех пор, пока не всплески не обязательно * правое «исправить» здесь, особенно когда потоковый (вместо буферизованного) подход настолько тривиален (см. Ответ Konrad) –

+0

Вы уверены, что вам нужны все эти данные в памяти однажды ... ? –

ответ

4

Ваши файлы могут быть небольшими, но Мастердата объект вы создаете по-прежнему будет хранить все данные, которые он находит в памяти ...

Похоже, вы, возможно, придется немного приложений редизайн делать ..

Пропустили ли вы это через профилировщик - посмотрите, можете ли вы изучить использование памяти в течение всей вашей программы?

+1

По меньшей мере две возможности: (1) Использовать x64 (при условии, что количество отдельных элементов не превышает 2^31) или (2) записать данные «MasterData» в файл и обработать позже запись за раз. –

+0

x64 работал !! Благодарю . – Kaushik

7

Используйте File.ReadLines вместо File.ReadAllLines, поскольку второй необязательно загружает весь файл в память, в то время как вам нужна только одна строка одновременно. MSDN says:

Когда вы используете ReadAllLines, вы должны дождаться возврата всего массива строк до того, как вы сможете получить доступ к массиву. Поэтому, когда вы работаете с очень большими файлами, ReadLines может быть более эффективным.

Это даст вам, вероятно, довольно большое улучшение памяти.

Вторая мысль - переосмыслить, если вам действительно нужны такие большие данные, которые хранятся в памяти. Возможно, вы можете просто сохранить путь к файлу в каждом файле и прочитать их по требованию.

+0

Иногда тривиальное изменение настолько поразительно эффективно. –

+0

Да, я изменил его и возникла такая же проблема. Отдельные файлы не очень большие (~ 650 КБ на файл). – Kaushik

+0

Поскольку эти данные выбрасываются после обработки каждого файла, это не будет иметь большого значения. – Guffa

2

Вы создаете объекты, которые содержат большую часть данных, которые вы читаете из файлов. Я думаю, что значения для категории, пользователя, кода ошибки, приложения и машины будут повторяться для большого количества записей.

Вы можете сделать словарь, содержащий эти строковые значения. Для каждого прочитанного значения вы должны проверить, существует ли в словаре уже равная строка и использовать ее вместо этого, иначе добавьте ее. Таким образом, эти строки будут существовать только один раз в памяти.

String interning использует тот же принцип, но вы ставите строки, которые они останутся в памяти, пока приложение не будет закрыто. Поместив их в словарь, вы сможете их удалить, когда они вам больше не понадобятся.

Пример; сделать словарь:

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

Получить значение и использование/зарегистрировать значение в словаре:

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

Вы бы, конечно, только использовать это на значения, где вы ожидаете, чтобы иметь много повторен значения.

+0

Это интересная оптимизация, о которой я никогда не думал. Было бы значительно уменьшить память, если она будет реализована. Благодаря! – Kaushik

Смежные вопросы