¿Cómo evito una pérdida de memoria con LINQ-To-SQL?


20

He tenido algunos problemas con LINQ-To-SQL en cuanto al uso de la memoria. Lo estoy usando en un servicio de Windows para hacer algo de procesamiento, y estoy revisando una gran cantidad de datos que estoy retirando del contexto. Sí, sé que podría hacer esto con un procedimiento almacenado, pero hay razones por las que sería una solución menos que ideal.

De todos modos, lo que veo básicamente es que la memoria no se libera incluso después de llamar al context.SubmitChanges(). Así que termino teniendo que hacer todo tipo de cosas extrañas, como solo retirar 100 registros a la vez, o crear varios contextos y hacer que todos ellos realicen tareas por separado. Si guardo el mismo DataContext y lo uso más tarde para otras llamadas, simplemente consume más y más memoria. Incluso si llamo al Clear() en la matriz "var tableRows" que me devuelve la consulta, configúrelo como nulo y llame al SYstem.GC.Collect(); aún así no libera la memoria.

Ahora he leído algo acerca de cómo usar DataContexts rápidamente y desecharlos rápidamente, pero parece que deberían ser una forma de forzar al contexto a volcar todos sus datos (o todos sus datos de seguimiento para un tabla particular) en un cierto punto para garantizar que la memoria sea gratuita.

¿Alguien sabe qué pasos garantizan que se libere la memoria?

15

Si no es necesario configurar el seguimiento de objetos DataContext.ObjectTrackingEnabled a falsa. Si lo necesita, puede usar el reflejo para llamar al DataContext.ClearCache(), aunque debe tener en cuenta que, dado que es interno, está sujeto a desaparecer en una versión futura del marco. Y hasta donde puedo decir, el framework en sí no lo usa, pero hace borra el caché de objetos.

+1

Tenga en cuenta que como dijeron los otros caballeros, probablemente sea mejor usar muchos DataContexts en esta situación. Pero, dado que la pregunta era cómo garantizar la liberación de memoria dentro de un contexto, el método ClearCache() está más cerca de la respuesta. 24 sep. 082008-09-24 14:43:13

  0

Sí, el uso o no de muchos datos depende de la cantidad de datos que tenga, puede aprender a manejar esto, aprendo de la serie RobConery MVC Road ... http://blog.wekeroad.com/category/mvc-storefront 04 sep. 092009-09-04 15:01:53

+3

Así es como se puede llamar ClearCache(): context.GetType() InvokeMember ( \t "ClearCache", \t BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, \t nula, el contexto, null);. 08 nov. 102010-11-08 16:42:34

  0

+1, aunque descubrí que necesitaba una bandera de enlace más para decirle que llame al método: BindingFlags.InvokeMethod. 11 nov. 102010-11-11 21:38:05

  0

intenté 'DataContext.ObjectTrackingEnabled to false', pero cuando ejecuto profiler de memoria 'indica que está dispuesto pero no puede ser GCed. 23 ene. 132013-01-23 09:58:32


20

Un DataContext realiza un seguimiento de todos los objetos que ha buscado. No lanzará esto hasta que sea basura recolectada. Además, como implementa IDisposable, debe llamar al Dispose o utilizar la instrucción using.

Este es el camino correcto a seguir:

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

6

Como señala David, debe deshacerse del DataContext utilizando un bloque de uso.

Parece que su principal preocupación es crear y eliminar un conjunto de objetos DataContext. Así es como está diseñado linq2sql. El DataContext está destinado a tener corta vida. Como está extrayendo una gran cantidad de datos de la base de datos, tiene sentido que haya mucho uso de memoria. Estás en el camino correcto, procesando tus datos en fragmentos.

No tenga miedo de crear una tonelada de DataContexts. Están diseñados para ser utilizados de esa manera.


3

Gracias a todos - Voy a ver el método ClearCache. Sólo una aclaración (para los futuros lectores), la situación en la que me estaba poniendo la usuage memoria era algo como esto:

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

Me acabo de encontrar con un problema similar. En mi caso, ayudó a establecer las propiedades de DataContext.ObjectTrackingEnabled en falso. Pero funciona sólo en el caso de iteración a través de las filas de la siguiente manera:

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

Si, por ejemplo, en la consulta para utilizar los métodos ToArray() o ToList() - ningún efecto