2015-09-30 2 views
1

Краткий обзор моей ситуации: Я получаю текстовые файлы по расписанию, которые разбиваются на количество символов, я разрабатываю приложение, которое разбивает каждую строку на список моделей в зависимости от которые обрабатывают его анализ. НАПРИМЕР. Текстовый файл «Лица» длиной 300 строк создаст список пользовательской модели «Человек». каждый элемент в списке - это строка в файле. Это делается через:Слишком много объектов, вызывающих из памяти исключение

private static List<Person> InitializePeople(DataManager dm, string filePath) 
    { 
     List<Person> people= new List<Person>(); 
      //Get lines from file to loop through 
      foreach (var line in dm.GetDataLinesFromFile(filePath).ToList()) 
      { 
       Person person = new Person(); 
       //This splits out the character counted split line 
       //into the models format and adds it to a list 
       people.Add((Person)dm.GetFormattedData(person, new FormatManger(), line)); 
      } 
     return people; 
    } 

Это работает нормально, пока я не получу файл длиной в миллионы строк. Как вы можете видеть, я совершаю нахальный NONO в цикле, в котором я создаю новый объект каждый раз, когда он петли. Это я верю в то, где моя проблема, она начинает бороться за память, когда создаются экземпляры объектов. Что это лучший способ сделать это? Какой-то другой способ, который более эффективен в работе с памятью? Любая помощь приветствуется.

+1

Да; не пытайтесь создать миллионы объектов. Вероятно, вы должны использовать базу данных. – SLaks

+5

Вам нужны все они в памяти одновременно? Если нет, используйте 'IEnumerable File.ReadLines()' и 'yield return', опустите' ToList() '. – CodeCaster

+0

Проблема заключается не в создании объекта внутри цикла, а в списке, который вы поддерживаете, который будет расти в зависимости от размера. Кстати, что вы делаете со списком? – XtremeBytes

ответ

2

Не материализовать данные (т.е. избежать ToList(), ToArray() и т.д.) работают только с IEnumerable.

Во-первых, проверьте dm.GetFormattedData выполнение должно быть что-то вроде

public IEnumerable<String> GetDataLinesFromFile(filePath) { 
    // Check that neither ReadAllLines nor ReadAllText is there 
    // Check absence of ToList() and ToArray() as well 
    return File 
     .ReadLines(filePath) // the only possible way of reading file 
     .Select(...) // possible, but not necessary part 
     .Where(...); // possible, but not necessary part 
    } 

затем перепроектировать InitializePeople в нечто подобное:

// Note IEnumerable<Person> instead of List<Person> 
    public static IEnumerable<Person> InitializePeople(DataManager dm, string filePath) { 
    //TODO: what's "nmy"? 
    //TODO: do you really want new FormatManger() for each People instance? 
    return GetDataLinesFromFile(filePath) 
     .Select(line => (Person)dm.GetFormattedData(nmy, new FormatManger(), line)); 
    } 

И так с помощью Linq вы можете иметь много гибкости, например

File.WriteAllLines(@"C:\MyFile.txt", InitializePeople(dm, @"C:\People.txt") 
    .Where(people => people.LastName == "Smith") 
    .Select(people => String.Format("{0} is {1} years old", people.FirstName, people.Age)); 
+0

Спасибо, плохо дайте это назад, можете ли вы объяснить, почему материализация данных плоха? – Srb1313711

+0

@ Srb1313711: это не плохо в общем случае, это плохо в * вашем случае *, потому что материализация создает список (ы) памяти в миллионах элементов. Я предлагаю * streaming *: элемент после обработки элемента. –

+0

Спасибо за это, это было очень полезно и похоже, что оно разрешило мою проблему! – Srb1313711

1

Список People слишком велик для хранения в памяти. Я предлагаю сделать это в кусках и обработать каждый кусок перед обработкой следующего. Если вы измените подпись своего метода, чтобы принять размер блока и позицию private static List<Person> InitializePeople(DataManager dm, string filePath, int chunkSize, out int position), вы можете достичь этого. Таким образом, вам не нужно будет читать весь файл, только пропустить его до определенной строки, а затем прочитать нужное количество записей.

Псевдокод будет выглядеть так:

var position = 0 
var people = [] 
while (count (people = InitializePeople(dataManager, file, 250, out position) > 0) 
    do something with people 
    position += count people 
+0

Спасибо за это, плохо дайте ему вихрь и дайте знать, как это происходит. – Srb1313711

+0

Я пробовал пройти этот маршрут, посчитав ответ Дмитрия немного более элегантным. Еще раз спасибо за помощь! – Srb1313711

+1

Согласен, его решение более элегантно. Рад, что вы исправили проблему. –

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