Недостаточно памяти при создании большого количества объектов C # - PullRequest
15 голосов
/ 28 апреля 2010

Я обрабатываю 1 миллион записей в своем приложении, которые извлекаю из базы данных MySQL. Для этого я использую Linq для получения записей и использую .Skip () и .Take () для обработки 250 записей одновременно. Для каждой полученной записи мне нужно создать от 0 до 4 элементов, которые я затем добавлю в базу данных. Таким образом, среднее количество предметов, которое должно быть создано, составляет около 2 миллионов.

IQueryable<Object> objectCollection = dataContext.Repository<Object>();
int amountToSkip = 0;
IList<Object> objects = objectCollection.Skip(amountToSkip).Take(250).ToList();
while (objects.Count != 0)
        {
            using (dataContext = new LinqToSqlContext(new DataContext()))
            {
                foreach (Object objectRecord in objects)
                {
                    // Create 0 - 4 Random Items
                    for (int i = 0; i < Random.Next(0, 4); i++)
                    {
                        Item item = new Item();
                        item.Id = Guid.NewGuid();
                        item.Object = objectRecord.Id;
                        item.Created = DateTime.Now;
                        item.Changed = DateTime.Now;
                        dataContext.InsertOnSubmit(item);
                    }
                }
                dataContext.SubmitChanges();
            }
            amountToSkip += 250;
            objects = objectCollection.Skip(amountToSkip).Take(250).ToList();
        }

Теперь проблема возникает при создании предметов. При запуске приложения ( и даже без использования dataContext ) объем памяти постоянно увеличивается. Как будто предметы никогда не выбрасываются. Кто-нибудь замечает, что я делаю не так?

Заранее спасибо!

Ответы [ 5 ]

7 голосов
/ 28 апреля 2010

Хорошо, я только что обсудил эту ситуацию с моим коллегой, и мы пришли к следующему решению, которое работает!

int amountToSkip = 0;
var finished = false;
while (!finished)
{
      using (var dataContext = new LinqToSqlContext(new DataContext()))
      {
           var objects = dataContext.Repository<Object>().Skip(amountToSkip).Take(250).ToList();
           if (objects.Count == 0)
                finished = true;
           else
           {
                foreach (Object object in objects)
                {
                    // Create 0 - 4 Random Items
                    for (int i = 0; i < Random.Next(0, 4); i++)
                    {
                        Item item = new Item();
                        item.Id = Guid.NewGuid();
                        item.Object = object.Id;
                        item.Created = DateTime.Now;
                        item.Changed = DateTime.Now;
                        dataContext.InsertOnSubmit(item);
                     }
                 }
                 dataContext.SubmitChanges();
            }
            // Cumulate amountToSkip with processAmount so we don't go over the same Items again
            amountToSkip += processAmount;
        }
}

В этой реализации мы каждый раз располагаем кэш Skip () и Take () и, таким образом, не теряем память!

5 голосов
/ 28 апреля 2010

Аааа, старая добрая InsertOnSubmit утечка памяти. Я сталкивался с этим и много раз ударился головой о стену, пытаясь загрузить данные из больших файлов CVS, используя LINQ to SQL. Проблема в том, что даже после вызова SubmitChanges, DataContext продолжает отслеживать все объекты, которые были добавлены с использованием InsertOnSubmit. Решением является SubmitChanges после определенного количества объектов, затем создайте новый DataContext для следующего пакета. Когда старый DataContext будет собирать мусор, то же самое можно сказать и о всех вставленных объектах, которые отслеживаются им (и которые вам больше не нужны).

"Но подожди!" Вы говорите: «Создание и утилизация многих DataContext будет иметь огромные накладные расходы!». Ну, нет, если вы создадите одно соединение с базой данных и передадите его каждому конструктору DataContext. Таким образом, поддерживается единственное соединение с базой данных, и в противном случае объект DataContext представляет собой легкий объект, который представляет собой небольшой рабочий блок и должен быть отброшен после его завершения (в вашем примере, отправив определенное количество записей) .

2 голосов
/ 28 апреля 2010

Мое лучшее предположение здесь - IQueryable, чтобы вызвать утечку памяти. Может быть, нет подходящей реализации для MySQL методов Take / Skip, и это делает пейджинг в памяти? Странные вещи произошли, но ваша петля выглядит хорошо. Все ссылки должны выходить за рамки и собирать мусор.

0 голосов
/ 28 апреля 2010

Вы пытались объявить Предмет вне цикла следующим образом:

IQueryable<Object> objectCollection = dataContext.Repository<Object>();
int amountToSkip = 0;
IList<Object> objects = objectCollection.Skip(amountToSkip).Take(250).ToList();
Item item = null;
while (objects.Count != 0)
        {
            using (dataContext = new LinqToSqlContext(new DataContext()))
            {
                foreach (Object objectRecord in objects)
                {
                    // Create 0 - 4 Random Items
                    for (int i = 0; i < Random.Next(0, 4); i++)
                    {
                        item = new Item();
                        item.Id = Guid.NewGuid();
                        item.Object = objectRecord.Id;
                        item.Created = DateTime.Now;
                        item.Changed = DateTime.Now;
                        dataContext.InsertOnSubmit(item);
                    }
                }
                dataContext.SubmitChanges();
            }
            amountToSkip += 250;
            objects = objectCollection.Skip(amountToSkip).Take(250).ToList();
        }
0 голосов
/ 28 апреля 2010

Ну да.

Итак, в конце этого цикла вы попытаетесь включить в свой список 2 миллиона элементов, не так ли? Сдается мне, что ответ тривиален: хранить меньше предметов или получить больше памяти.

- Изменить:

Возможно, я прочитал это неправильно, мне, вероятно, нужно было бы скомпилировать и протестировать это, но я не могу сделать это сейчас. Я оставлю это здесь, но я могу ошибаться, я недостаточно тщательно его рассмотрел, чтобы быть окончательным, тем не менее ответ может оказаться полезным или нет. (Судя по понижению, я не думаю: P)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...