Как избежать утечки памяти с помощью LINQ-To-SQL?


20

У меня возникли проблемы с LINQ-To-SQL при использовании памяти. Я использую его в службе Windows для выполнения некоторой обработки, и я просматриваю большое количество данных, которые я отвлекаю от контекста. Да. Я знаю, что могу сделать это с помощью хранимой процедуры, но есть причины, по которым это было бы менее идеальным решением.

В любом случае, то, что я вижу в основном, это память не выпускается даже после того, как я звоню context.SubmitChanges(). Поэтому мне приходится делать всевозможные странные вещи, например, удалять только 100 записей во времени или создавать несколько контекстов и каждый из них выполнять отдельные задачи. Если я сохраню тот же DataContext и буду использовать его позже для других вызовов, он просто поглощает все больше и больше памяти. Даже если я назову Clear() в массиве «var tableRows», который возвращает запрос мне, установите для него значение null и вызовите SYstem.GC.Collect() - он все еще не освобождает память.

Теперь я прочитал некоторые сведения о том, как вы должны быстро использовать DataContexts и быстро избавиться от них, но кажется, что они должны быть способом заставить контекст сбросить все свои данные (или все его данные отслеживания для конкретную таблицу) в определенный момент, чтобы гарантировать, что память свободна.

Кто-нибудь знает, какие шаги гарантируют освобождение памяти?

15

Если вам не нужно отслеживать объект установить DataContext.ObjectTrackingEnabled к ложной. Если вам это нужно, вы можете использовать отражение для вызова внутреннего DataContext.ClearCache(), хотя вы должны знать, что с его внутренней стороны он может исчезнуть в будущей версии фреймворка. И насколько я могу судить, сама структура не использует его, но делает очищает кеш объекта.

+1

Заметьте, что как говорили другие джентльмены, вероятно, лучше использовать многие DataContexts в этой ситуации. Но поскольку вопрос заключался в том, как гарантировать освобождение памяти в одном контексте, метод ClearCache() ближе к ответу. 24 сен. 082008-09-24 14:43:13

  0

Да, используйте или не много данных datacontext зависит от вашего объема данных, вы можете узнать, как с этим справиться, я узнаю из серии RobConery MVC Road ... http://blog.wekeroad.com/category/mvc-storefront 04 сен. 092009-09-04 15:01:53

+3

Вот как вы можете позвонить ClearCache(): context.GetType() InvokeMember ( \t "ClearCache", \t BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, \t нуль, контекст, нуль);. 08 ноя. 102010-11-08 16:42:34

  0

+1, хотя я обнаружил, что мне нужен еще один флаг привязки, чтобы сказать ему, чтобы вызвать метод: BindingFlags.InvokeMethod. 11 ноя. 102010-11-11 21:38:05

  0

Я попытался «DataContext.ObjectTrackingEnabled to false», но когда я запускаю профайлер памяти, «это указывает на то, что он удален, но не может быть GCed. 23 янв. 132013-01-23 09:58:32


20

DataContext отслеживает все объекты, которые он когда-либо получал. Он не освободит его, пока не будет собран мусор. Кроме того, поскольку он реализует IDisposable, вы должны позвонить Dispose или использовать заявление using.

Это правильный путь:

using(DataContext myDC = new DataContext) 
{ 
    // Do stuff 
} //DataContext is disposed 

6

Как Дэвид указывает, вы должны избавиться от DataContext с использованием используемого блока.

Похоже, что ваша главная задача заключается в создании и удалении кучи объектов DataContext. Именно так разработан linq2sql. DataContext имеет короткий срок службы. Поскольку вы извлекаете много данных из БД, имеет смысл, что будет много использования памяти. Вы находитесь на правильном пути, обрабатывая свои данные в кусках.

Не бойтесь создавать тонны DataContexts. Они предназначены для использования таким образом.


3

Спасибо, ребята, я проверю метод ClearCache. Просто для уточнения (для будущих читателей), ситуация, в которой я получал Usuage памяти было что-то вроде этого:

using(DataContext context = new DataContext()) 
{ 
    while(true) 
    { 
     int skipAmount = 0; 
     var rows = context.tables.Select(x => x.Dept == "Dept").Skip(skipAmount).Take(100); 

     //break out of loop when out of rows 

     foreach(table t in rows) 
     { 
     //make changes to t 
     } 

     context.SubmitChanges(); 
     skipAmount += rows.Count(); 

     rows.Clear(); 
     rows = null; 

     //at this point, even though the rows have been cleared and changes have been 
     //submitted, the context is still holding onto a reference somewhere to the 
     //removed rows. So unless you create a new context, memory usuage keeps on growing 
    } 
} 

0

Я просто столкнулся с подобной проблемой. В моем случае, помогли установить свойства DataContext.ObjectTrackingEnabled на false. Но это работает только в том случае, перебором строк следующим образом:

using (var db = new DataContext()) 
{ 
    db.ObjectTrackingEnabled = false; 
    var documents = from d in db.GetTable<T>() 
        select d; 
    foreach (var doc in documents) 
    { 
     ... 
    } 
} 

Если, например, в запросе использовать методы ToArray() или ToList() - никакого эффекта