2010-11-04 3 views
0

У меня есть следующий метод для циклического перемещения по таблице, изменение некоторых значений в каждой строке и сохранение chages обратно в базу данных. Чтобы ускорить работу, я получаю данные в наборах из 10 000 строк. Это большая таблица с более чем 25 миллионами записей.Как освободить память?

Проблема в том, что мое приложение, похоже, не освобождает память. Я попробовал переопределить переменную records до nothing или явно раскрыть сборщик мусора, но память остается там. Запуск встроенного профилирования VS10 Я вижу, что виновником является метод system.linq.enumerable.tolist(), который занимает более 98% моей памяти. Как освободить эту память после вызова saveChanges?

db = New databaseEntities 
Dim size = 25000000 
Dim stepSize = 10000 
For i = 0 to size Step stepSize 
    Dim sql = (From A In db.table).OrderBy(Function(A) A.Column).Skip(i).Take(stepSize) 
    Dim records As New List(Of table) 
    records = sql.ToList 
    For Each record In records 
    'do some work 
    Next 
    db.SaveChanges() 
    records = Nothing 
    GC.Collect() 
Next 

ответ

0

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

Вариант 1), если «выполнить некоторую работу» не влияет на порядок, в который вы должны были возвращать записи, вы могли бы перемещать создание объектов базы данных внутри цикла For и объявлять его с помощью блока использования. Это должно приводить к тому, что каждый блок сущностей будет освобождаться каждый раз по циклу for

Вариант 2) Если ваша операция по существу параллельна, и то, что вы делаете с одним «сущностным» объектом, не имеет каких-либо зависимостей ни от какого другого, то вы можете вызвать databaseEntities.Detach (запись) после db.SaveChanges, что позволит сборщику мусора вернуть пространство сущности.

Глядя на ваш код, я подозреваю, что любой из thse может быть использован

+0

Вариант 2 очень медленный. Занимает около минуты, чтобы отделить 10 000 записей (в сравнении обновление их занимает около 2 секунд). Так что, я думаю, я собираюсь пойти с опцией 1. – Pavel

0

может быть, вы можете попробовать это: (я не проверял)

db.SaveChanges() 
For Each record In records 
    record.dispose ''only if class table got a dispose method 
Next 
records.clear 
records = Nothing 
+0

Нет, в объекте таблицы не существует объектов. – Pavel

+0

@Pavel хорошо, может быть, вы могли бы попробовать ясный метод? – Fredou

0

Я ни в коем случае не Linq эксперта SQL, но я думаю, что DataContext кэширует все строки, прочитайте, чтобы вы либо сбросили кеш, либо потеряли ссылку на DataContext.

+0

Я думаю, что у вас есть что-то здесь. Если я объявлю новый instabne db внутри цикла for for, проблема с памятью исчезнет. Однако я не думаю, что это очень хорошее решение. Должен быть некоторый способ сообщить диспетчеру сущности очистить кеш. – Pavel

+0

Речь идет не о диспетчере памяти, а о DataContext. Вы должны сообщить DataContext очистить свой кеш, и диспетчер памяти вернет память, когда захочет. – erikkallen

+0

@Pavel, делая действия на большом количестве записей - это немного слабость EF. Если бы я выполнял одно и то же действие по большому числу объектов, а производительность была приоритетом, я бы, скорее всего, использовал хранимую процедуру. Это означает, что некоторые из ваших бизнес-логик теперь находятся в базе данных вместо модели, но иногда мы должны прагматично относиться к этим вещам. –

0

Если вам не нужно обновление сущности, используйте MergeOption.NoTracking. Контекст больше не будет содержать ссылку на объект, и он не будет исправлять.

+0

Правда, хотя факт, что он загружает объекты таблицы (и, видимо, ничего больше), выполняет некоторую работу, а затем вызывает db.SaveChanges имеет тенденцию подразумевать, что ему нужно отслеживать изменения. –

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