2010-11-04 5 views
0

J'ai la méthode suivante pour faire une boucle dans une table, changer certaines valeurs dans chaque ligne et sauvegarder les chages dans la base de données. Pour accélérer les choses, je récupère des données dans des ensembles de 10 000 lignes. C'est une grande table avec plus de 25 millions d'enregistrements.Comment libérer la mémoire?

Le problème est que mon application ne semble pas libérer de mémoire. J'ai essayé de redéclarer la variable records à nothing ou d'appeler explicitement le garbage collector mais la mémoire reste là. En exécutant le profileur VS10 intégré, je peux voir que le coupable est la méthode system.linq.enumerable.tolist() qui occupe plus de 98% de ma mémoire. Comment puis-je libérer cette mémoire après l'appel à 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 

Répondre

0

Le référentiel contient une référence à chaque entité qu'il suit, de sorte que vous ne serez pas en mesure de disposer d'une entité tant que le référentiel est en ligne et de le suivre. Cela signifie que vous devez soit disposer du référentiel, soit détacher chaque entité une fois que vous avez fini de le traiter.

Option 1) si "faire un peu de travail" n'affecte pas l'ordre dans lequel vous retourneriez les enregistrements, vous pouvez déplacer la création de databaseEntities dans la boucle For, et le déclarer avec un bloc using. Cela devrait provoquer la libération de chaque bloc d'entités à chaque fois que la boucle est

Option 2) Si votre opération est essentiellement parallèle, et que ce que vous faites à une entité "table" ne dépend d'aucune autre, alors vous pouvez appeler databaseEntities.Detach (enregistrement) après db.SaveChanges, ce qui permettra au garbage collector de récupérer l'espace de l'entité.

En regardant votre code, je soupçonne que l'une des thse pourrait être utilisé

+0

L'option 2 est très lente. Prend environ une minute pour détacher 10 000 enregistrements (en comparaison, leur mise à jour prend environ 2 secondes). Donc je suppose que je vais aller avec l'option 1. – Pavel

0

vous pouvez peut-être essayer ceci: (je ne pas testé)

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

Non, il n'y a pas de propriétés de disposition dans l'objet de table. – Pavel

+0

@Pavel ok, peut-être que vous pourriez essayer la méthode claire? – Fredou

0

Je ne suis pas un Linq à l'expert SQL, mais je pense que le DataContext met en cache toutes les lignes que vous avez lu donc vous devez vider le cache ou perdre votre référence au DataContext.

+0

Je pense que vous avez quelque chose ici. Si je déclare une nouvelle instacne de db dans la boucle 'for', le problème de mémoire disparaît. Cependant, je ne pense pas que ce soit une très bonne solution. Il devrait y avoir un moyen de dire au gestionnaire d'entités de vider le cache. – Pavel

+0

Il ne s'agit pas du gestionnaire de mémoire, mais du DataContext. Vous devez indiquer au DataContext d'effacer son cache et le gestionnaire de mémoire récupère la mémoire quand il le souhaite. – erikkallen

+0

@Pavel, faire des actions sur un grand nombre d'enregistrements est un peu une faiblesse d'EF. Si je faisais la même action sur un grand nombre d'entités et que les performances étaient prioritaires, j'utiliserais probablement une procédure stockée à la place. Cela signifie qu'une partie de votre logique métier est maintenant dans la base de données au lieu du modèle, mais parfois nous devons être pragmatiques à propos de ces choses. –

0

Si vous ne avez pas besoin de mise à jour les entités, utilisez MergeOption.NoTracking. Le contexte ne gardera plus une référence à l'entité, et ne le fera pas non plus.

+0

Vrai, bien que le fait qu'il charge des entités de table (et apparemment rien d'autre), fonctionne un peu, puis appelle db.SaveChanges a tendance à laisser entendre qu'il a besoin de suivre les changements. –

Questions connexes