Interrogation des données d’entité et fuite de mémoire

Je télécharge beaucoup de données en boucle, mais après quelques opérations, je les supprime, mais ce que je vois, c’est que l’allocation de mémoire augmente très rapidement, quelques secondes et 1 Go.

using (var contex = new DB) { var inputs = contex.AIMRInputs.Where(x => x.Input_Type == 1); foreach (var input in inputs) { var data = contex.Values.Where(x => x.InputID == input.InputID).OrderBy(x => x.TimeStamp).ToList(); if (data.Count == 0) continue; foreach (var value in data) { Console.WriteLine(Value.property); } data.Clear(); } } 

La première chose que vous pouvez faire est de désactiver le suivi des modifications, car vous ne modifiez aucune donnée dans votre code. Cela évite que les objects chargés ne soient attachés au contexte:

Pour DbContext (EF> = 4.1):

 var inputs = contex.AIMRInputs.AsNoTracking() .Where(x => x.Input_Type == 1); 

Et:

 var data = contex.Values.AsNoTracking() .Where(x => x.InputID == input.InputID) .OrderBy(x => x.TimeStamp) .ToList(); 

modifier

Pour EF 4.0, vous pouvez laisser vos requêtes telles quelles mais append les éléments suivants comme les deux premières lignes du bloc using :

 contex.AIMRInputs.MergeOption = MergeOption.NoTracking; contex.Values.MergeOption = MergeOption.NoTracking; 

Cela désactive le suivi des modifications pour ObjectContext .

Modifier 2

… mentionnant en particulier le commentaire de @James Reategui selon lequel AsNoTracking réduit l’encombrement de la mémoire:

Ceci est souvent vrai (comme dans le modèle / requête de cette question) mais pas toujours! En fait, utiliser AsNoTracking peut être contre-productif en ce qui concerne l’utilisation de la mémoire.

Que fait AsNoTracking lorsque des objects sont matérialisés en mémoire?

  • Premièrement: il n’attache pas l’entité au contexte et ne crée donc pas d’entrées dans le gestionnaire d’état du contexte. Ces entrées consumnt de la mémoire. Lors de l’utilisation de POCO, les entrées contiennent un instantané des valeurs de propriété de l’entité lors de son chargement / attachement au contexte – il s’agit en principe d’une copie de toutes les propriétés (scalaires) en plus de l’object lui-même. Ainsi, la mémoire consommée prend (environ) deux fois plus que la taille de l’object lorsque AsNoTracking n’est pas appliqué.

  • Deuxièmement: lorsque des entités ne sont pas attachées au contexte, EF ne peut pas tirer parti de l’avantage du mappage d’identité entre valeurs de clé et identités de référence d’object. Cela signifie que les objects avec la même clé seront matérialisés plusieurs fois, ce qui occupera plus de mémoire sans utiliser AsNoTracking EF garantira qu’une entité ne sera matérialisée qu’une fois par valeur de clé.

Le deuxième sharepointvient particulièrement important lorsque des entités liées sont chargées. Exemple simple:

Dites, nous avons une Order et une entité Customer et une commande a une commande client. Disons que l’object Order a la taille 10 octets et l’object Customer la taille 20 octets. Maintenant, nous lançons cette requête:

 var orderList = context.Orders .Include(o => o.Customer).Take(3).ToList(); 

Et supposons que toutes les 3 commandes chargées aient le même client atsortingbué. Parce que nous n’avons pas désactivé le suivi, EF va se matérialiser:

  • 3 objects de commandes = 3×10 = 30 octets
  • 1 object client = 1×20 = 20 octets (car le contexte reconnaît que le client est le même pour les 3 commandes, il ne matérialise qu’un seul object client)
  • 3 entrées d’instantané d’ordre avec des valeurs d’origine = 3×10 = 30 octets
  • 1 entrée d’instantané client avec les valeurs d’origine = 1×20 = 20 octets

Somme: 100 octets

(Par souci de simplicité, je suppose que les entrées de contexte avec les valeurs de propriété copiées ont la même taille que les entités elles-mêmes.)

Maintenant, nous lançons la requête avec un suivi des modifications désactivé:

 var orderList = context.Orders.AsNoTracking() .Include(o => o.Customer).Take(3).ToList(); 

Les données matérialisées sont:

  • 3 objects de commandes = 3×10 = 30 octets
  • 3 (!) Objets client = 3×20 = 60 octets (pas de mappage d’identité = plusieurs objects par clé, les trois objects client auront les mêmes valeurs de propriété, mais il rest trois objects en mémoire)
  • Aucune entrée d’instantané

Somme: 90 octets

Ainsi, l’utilisation de la requête AsNoTracking consum 10 octets de mémoire en moins dans ce cas.

Maintenant, le même calcul avec 5 commandes ( Take(5) ), encore une fois, toutes les commandes ont le même client:

Sans AsNoTracking :

  • 5 objects de commandes = 5×10 = 50 octets
  • 1 object client = 1×20 = 20 octets
  • 5 entrées d’instantané de commande avec les valeurs d’origine = 5×10 = 50 octets
  • 1 entrée d’instantané client avec les valeurs d’origine = 1×20 = 20 octets

Somme: 140 octets

Avec AsNoTracking :

  • 5 objects de commandes = 5×10 = 50 octets
  • 5 (!) Objets client = 5×20 = 100 octets
  • Aucune entrée d’instantané

Somme: 150 octets

Cette fois, l’utilisation d’ AsNoTracking était 10 octets de plus.

Les chiffres ci-dessus sont très approximatifs, mais il existe un seuil de rentabilité où l’utilisation d’ AsNoTracking peut nécessiter plus de mémoire.

La différence de consommation de mémoire entre l’utilisation d’ AsNoTracking ou non dépend fortement de la requête, des relations dans le modèle et des données concrètes chargées par la requête. Par exemple: AsNoTracking toujours mieux en mémoire lorsque les commandes de l’exemple ci-dessus (ou principalement) ont des clients différents.

Conclusion: AsNoTracking est principalement conçu comme un outil permettant d’améliorer les performances des requêtes, et non l’utilisation de la mémoire. Dans de nombreux cas, cela consumra également moins de mémoire. Mais ne soyez pas surpris si une requête spécifique nécessite plus de mémoire avec AsNoTracking . En fin de compte, vous devez mesurer l’empreinte mémoire pour une décision solide en faveur ou contre AsNoTracking .

Une partie si le problème ici pourrait concerner le DataContext. La plupart d’entre eux mettent en cache des informations ou stockent des informations supplémentaires au fur et à mesure de l’exécution de vos requêtes. Par conséquent, l’empreinte mémoire augmente progressivement. Je vérifierais d’abord avec un profileur, mais si tel est votre problème, vous devrez peut-être recréer un nouveau contexte de données après chaque demande X (essayez différentes valeurs de X pour voir ce qui fonctionne le mieux).

J’aimerais également noter que la plupart des gens ont tendance à avoir beaucoup de mémoire. Avant de commencer à utiliser ces types d’optimisation, assurez-vous que vous utilisez plus de mémoire que ce qui est réellement acceptable. Le CPG commence également à effacer la mémoire de manière plus agressive, car vous disposez de moins de mémoire libre. Cela ne dérange pas d’optimiser prématurément (et vous ne devriez pas non plus).