web-dev-qa-db-fra.com

Une exception de type 'System.OutOfMemoryException' a été levée

Fondamentalement, j'utilise Entity Framework pour interroger une énorme base de données. Je souhaite renvoyer une liste de chaînes, puis la consigner dans un fichier texte.

List<string> logFilePathFileName = new List<string>();
var query = from c in DBContext.MyTable where condition = something select c;
foreach (var result in query)
{
    filePath = result.FilePath;
    fileName = result.FileName;
    string temp = filePath + "." + fileName;
    logFilePathFileName.Add(temp);
    if(logFilePathFileName.Count %1000 ==0)
        Console.WriteLine(temp+"."+logFilePathFileName.Count);
}

Cependant, j'ai eu une exception lorsque logFilePathFileName.Count=397000. L'exception est:

Une exception de type 'System.OutOfMemoryException' a été levée.

Une exception de première chance du type 'System.OutOfMemoryException' S'est produite dans System.Data.Entity.dll

METTRE À JOUR:

Ce que je veux utiliser une requête différente, dites: sélectionnez 1000 premiers puis ajoutez à la liste, mais je ne sais pas après 1000 alors quoi?

7
user1108948

Le plus probablement, il ne s'agit pas d'une RAM en l'état, donc augmenter votre RAM ou même compiler et exécuter votre code dans la machine 64 bit n'aura pas d'effet positif dans ce cas. 

Je pense que cela est lié au fait que les collections de .NET sont limitées à un maximum de 2GB RAM espace (aucune différence ni 32 ni 64 bit).

Pour résoudre ce problème, scindez votre liste en morceaux beaucoup plus petits et probablement votre problème aura disparu.

Juste une solution possible:

foreach (var result in query)
{
    ....
    if(logFilePathFileName.Count %1000 ==0) {
        Console.WriteLine(temp+"."+logFilePathFileName.Count);
        //WRITE SOMEWHERE YOU NEED 
        logFilePathFileName = new List<string>(); //RESET LIST !|
    }
}

MODIFIER 

Si vous voulez fragmenter une query , vous pouvez utiliser Skip(...) et Take(...)

Juste un exemple explicatif: 

var fisrt1000 = query.Skip(0).Take(1000);
var second1000 = query.Skip(1000).Take(1000);

...etc.. 

Naturellement, mettez-le dans votre itération et paramétrez-le sur la base de données que vous connaissez ou dont vous avez besoin.

13
Tigran

Pourquoi collectez-vous les données dans un List<string> si tout ce que vous avez à faire est de les écrire dans un fichier texte?

Vous pourriez aussi bien juste:

  • Ouvrez le fichier texte.
  • Parcourez les enregistrements en ajoutant chaque chaîne au fichier texte (sans stocker les chaînes en mémoire);
  • Rincer et fermer le fichier texte.

Vous aurez besoin de beaucoup moins de mémoire que maintenant, car vous ne garderez pas toutes ces chaînes inutilement en mémoire.

3
Roy Dictus

Vous aurez probablement besoin de définir quelques vmargs pour la mémoire!

1
Bob Flannigon

Ce que dit Roy Dictus semble la meilleure solution. Vous pouvez également essayer d’ajouter une limite à votre requête. Donc, le résultat de votre base de données ne sera pas si grand.

Pour plus d'informations sur: Limitation de la taille de la requête avec un framework d'entités

1
Jeroen van Veghel

Vous ne devriez pas lire tous les enregistrements de la base de données à la liste. Cela demandait beaucoup de mémoire. Vous pouvez combiner des enregistrements de lecture et les écrire dans un fichier. Par exemple, lisez 1000 enregistrements de la base de données dans une liste et enregistrez-les (ajoutez-les) dans un fichier texte, effacez la mémoire utilisée (list.Clear ()) et continuez avec les nouveaux enregistrements.

0
Warr

J'avais l'habitude d'utiliser l'arraylist gc dans VS c ++, semblable à la liste gc que vous avez utilisée, pour travailler avec des ensembles de données petits et intermédiaires, mais lors de l'utilisation de Big Dat, le même problème, 'System.OutOfMemoryException', était lancé. Comme la taille de ces gcs ne peut excéder 2 Go et que, par conséquent, le Big Data devient inefficace, j'ai construit ma propre liste chaînée, qui offre les mêmes fonctionnalités, une augmentation dynamique et un index obtenu. En gros, il s'agit d'une classe de liste chaînée normale, avec tableau dynamique à l'intérieur pour fournir des données par index, il duplique l'espace, mais vous pouvez supprimer la liste liée après la mise à jour du tableau si vous n'en avez pas besoin en conservant uniquement le tableau dynamique, cela résoudrait le problème. voir le code:

struct LinkedNode
{
    long data;
    LinkedNode* next;
};


class LinkedList
{
public:
    LinkedList();
    ~LinkedList();
    LinkedNode* head;
    long Count;
    long * Data;
    void add(long data);
    void update();
    //long get(long index);
};

LinkedList::LinkedList(){
    this->Count = 0;
    this->head = NULL;
}

LinkedList::~LinkedList(){
    LinkedNode * temp; 
    while(head){
        temp= this->head ;
        head = head->next;
        delete temp;
    }
    if (Data)
        delete [] Data; Data=NULL;
}

void LinkedList::add  (long data){
    LinkedNode * node = new LinkedNode();
    node->data = data;
    node->next = this->head;
    this->head = node;
    this->Count++;}

void LinkedList::update(){
    this->Data= new long[this->Count];
    long i = 0;
    LinkedNode * node =this->head;
    while(node){
        this->Data[i]=node->data;
        node = node->next;
        i++;
    }
}

Si vous l'utilisez, référez-vous à mon travail https://www.liebertpub.com/doi/10.1089/big.2018.0064

0
Ahmad Hassanat

J'ai lu dans plusieurs autres rubriques de StackOverflow qu'Entity Framework n'est pas conçu pour gérer des données en vrac de cette manière. L'EF mettra en cache/suivra toutes les données dans le contexte et provoquera une exception dans les cas de gros volumes de données. Les options consistent à utiliser directement le code SQL ou à fractionner vos enregistrements en ensembles plus petits.

0
Abbas