Wie vermeide ich einen Speicherverlust mit LINQ-To-SQL?


20

Ich habe einige Probleme mit LINQ-To-SQL rund um die Speichernutzung. Ich benutze es in einem Windows-Dienst, um etwas zu verarbeiten, und ich durchlaufe eine große Menge an Daten, die ich aus dem Kontext zurückziehe. Ja - Ich weiß, dass ich dies mit einer gespeicherten Prozedur tun könnte, aber es gibt Gründe, warum dies eine weniger als ideale Lösung wäre.

Wie auch immer, was ich im Grunde sehe ist Speicher wird nicht freigegeben, auch nachdem ich context.SubmitChanges() anrufen. Ich muss also alle möglichen seltsamen Dinge tun, wie zum Beispiel 100 Datensätze zu einem bestimmten Zeitpunkt zurückziehen, oder mehrere Kontexte erstellen und sie alle getrennte Aufgaben ausführen lassen. Wenn ich dasselbe DataContext behalte und es später für andere Anrufe verwende, verschlingt es nur mehr und mehr Speicher. Auch wenn ich Clear() auf dem Array "var tableRows" aufrufen, die die Abfrage an mich zurückgibt, setzen Sie es auf null, und rufen Sie SYstem.GC.Collect() auf - es gibt den Speicher noch nicht frei.

Jetzt habe ich etwas darüber gelesen, wie Sie DataContexts schnell verwenden und sie schnell beseitigen sollten, aber es scheint, als ob sie eine Möglichkeit sein könnten, den Kontext zu zwingen, alle seine Daten (oder alle seine Tracking-Daten für eine bestimmte Tabelle) an einem bestimmten Punkt zu garantieren, dass der Speicher frei ist.

Wer weiß, welche Schritte garantieren, dass der Speicher freigegeben wird?

15

Wenn Sie setzen DataContext.ObjectTrackingEnabled-falsch keine Objektverfolgung benötigen. Wenn Sie es brauchen, können Sie die interne DataContext.ClearCache() intern mit Reflection aufrufen, obwohl Sie wissen müssen, dass es seit seiner internen in einer zukünftigen Version des Frameworks verschwindet. Und soweit ich das beurteilen kann, verwendet das Framework selbst es nicht, aber es löscht den Objektcache.

+1

Beachten Sie, dass Wie die anderen Herren schon sagten, ist es wahrscheinlich besser, in dieser Situation viele DataContexte zu verwenden. Aber da die Frage war, wie die Freigabe von Speicher in einem Kontext garantiert werden kann, ist die ClearCache() -Methode näher an der Antwort. 24 sep. 082008-09-24 14:43:13

  0

Ja, verwenden Sie oder nicht viele Datenkontext hängt von Ihrer Menge an Daten ab, können Sie lernen, damit umzugehen, lerne ich von RobConery MVC Road-Serie ... http://blog.wekeroad.com/category/mvc-storefront 04 sep. 092009-09-04 15:01:53

+3

hier ist, wie Sie anrufen können Clearcache(): context.GetType() InvokeMember ( \t "Clearcache", \t BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, \t null, Kontext, null);. 08 nov. 102010-11-08 16:42:34

  0

+1, obwohl ich festgestellt habe, dass ich ein weiteres Bindungsflag benötigt habe, um es anzuweisen, die Methode aufzurufen: BindingFlags.InvokeMethod. 11 nov. 102010-11-11 21:38:05

  0

Ich habe 'DataContext.ObjectTrackingEnabled auf false' versucht, aber wenn ich den Speicher-Profiler starte ', wird dies als entsorgt angezeigt, kann aber nicht erkannt werden. 23 jan. 132013-01-23 09:58:32


20

Ein DataContext verfolgt alle Objekte, die er jemals abgerufen hat. Es wird dies nicht freigeben, bis es Müll gesammelt wird. Sie müssen auch IDisposable implementieren Dispose oder die using Anweisung verwenden.

Dies ist der richtige Weg zu gehen:

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

6

Wie David hervorhebt, sollten Sie den DataContext über einen using-Block entsorgen.

Es scheint, dass es in erster Linie darum geht, eine Reihe von DataContext-Objekten zu erstellen und zu verteilen. So ist linq2sql entworfen. Der DataContext soll eine kurze Lebensdauer haben. Da Sie viele Daten aus der DB ziehen, ist es sinnvoll, dass viel Speicher belegt wird. Sie sind auf dem richtigen Weg, indem Sie Ihre Daten in Blöcken verarbeiten.

Haben Sie keine Angst, eine Menge DataContexte zu erstellen. Sie sind so konzipiert, dass sie so verwendet werden können.


3

Danke Jungs - Ich werde die ClearCache-Methode überprüfen. Nur zur Klarstellung (für zukünftige Leser), die Situation, in der ich war die Erinnerung usuage bekommen war so etwas wie diese:

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

Ich lief in ein ähnliches Problem. In meinem Fall half, die Eigenschaften von DataContext.ObjectTrackingEnabled auf false zu etablieren. Aber es funktioniert nur in dem Fall durch die Reihen der Iteration wie folgt:

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

Wenn zum Beispiel in der Abfrage die Methoden ToArray() oder ToList() verwenden - keine Wirkung