IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Framework .NET Discussion :

Entity Framework Problème mémoire OutOfMemoryException


Sujet :

Framework .NET

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    33
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 33
    Points : 23
    Points
    23
    Par défaut Entity Framework Problème mémoire OutOfMemoryException
    Bonjour,

    j'ai un problème tout simple : je veux charger en base un nombre relativement important ( ~50 000) de données dans une seule table ( TAB ).
    J'utilise Entity Framework.

    Le problème est que la mémoire augmente sans cesse jusqu'à exploser.
    J'ai essayé de découper mon chargement en plusieurs appels à la BDD, et ça ne marche pas.
    En fait la mémoire augmente après chaque "SaveChanges()" et ne redescend plus.

    J'ai l'impression (mais c'est une impression) que, comme je conserve un pointeur sur les objets que je viens de charger en base, Entity Framework les "mémorise" et du coup utilise de la mémoire.
    Mais après, je ne vais pas toucher à ces objets, j'aimerais que Entity Framework les "oublie" une fois entrés en base.

    Quelle est la bonne pratique ?

    Et je précise, forcer le garbage collector n'arrange en rien.

    Voilà un exemple de code, où j'ai essayé de découper "par paquets de 1000" :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    public static void sauvegarde_TAB(List<Entitees.TAB> lgec)
            {
    // en entrée de la fonction List<TAB> lgec 
    // contenant une liste des objets à charger
     for (int i = 0; i < lgec.Count; )
                {
                    using (Entitees.Entities db = new Entitees.Entities())
                    {
                        for (int j = 0; j < 1000 && i < lgec.Count; j++)
                        {
                            db.TAB.AddObject(lgec[i]);
                            i++;
                        }
                        db.SaveChanges();
                    }
                }
    }

  2. #2
    Membre confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2011
    Messages
    269
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 269
    Points : 460
    Points
    460
    Par défaut
    Bonjour

    Déjà je vois pas de cause spécifique à ton erreur dans ton code.
    Ce type de code devrait passer avec un nombre d'insert bcp plus grand.
    Sauf si une instance de Entitees.TAB pèse vraiment bcp en mémoire.

    Citation Envoyé par losformen
    J'ai l'impression (mais c'est une impression) que, comme je conserve un pointeur sur les objets que je viens de charger en base, Entity Framework les "mémorise" et du coup utilise de la mémoire.
    Pour la mémoirisation c'est vrai et c'est faux. En effet Entity fait un tracage des objet pour savoir s'ils ont changé ou non. Mais dans ton cas, ton objet est déjà maintenant en mémoire par une List<>.
    Mais il me semble que ce tracage n'intervient qu'en cas de requête de lecture, pas sur les requêtes d'insert.

    Pour la bonne pratique, tu as raison de forcer le commit sur le SQL tous les n insert. Personnellement je fais ça avec un calcul de modulo, mais ta double bloucle fonctionne aussi.

    Tu pourrais nous données la description de Entitees.TAB?
    Y'a peut-être un loup de se coté là.

  3. #3
    Membre à l'essai
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    33
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 33
    Points : 23
    Points
    23
    Par défaut
    Pour la description de Entitiees.Tab :
    En gros, c'est l'entité générée automatiquement d'après la table de base de donnée (Oracle) qui ressemble à :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
     
    Col1	VARCHAR2(100 BYTE)
    Col2	VARCHAR2(100 BYTE)
    Col3	VARCHAR2(100 BYTE)
    Col4	VARCHAR2(100 BYTE)
    Col5	VARCHAR2(100 BYTE)
    Col6	VARCHAR2(100 BYTE)
    Col7	VARCHAR2(100 BYTE)
    Col8	VARCHAR2(100 BYTE)
    Col9	VARCHAR2(100 BYTE)
    Col10	VARCHAR2(100 BYTE)
    Col11	VARCHAR2(100 BYTE)
    Col12	VARCHAR2(100 BYTE)
    Col13	VARCHAR2(100 BYTE)
    Col14	VARCHAR2(100 BYTE)
    Col15	VARCHAR2(100 BYTE)
    Col16	VARCHAR2(100 BYTE)
    Col17	VARCHAR2(100 BYTE)
    Col18	VARCHAR2(100 BYTE)
    Col19	VARCHAR2(100 BYTE)
    Col20	VARCHAR2(100 BYTE)
    Col21	DATE
    Col22	VARCHAR2(100 BYTE)
    Col23	VARCHAR2(100 BYTE)
    Col24	VARCHAR2(100 BYTE)
    Col25	VARCHAR2(100 BYTE)
    Col26	VARCHAR2(100 BYTE)
    Col27	DATE
    Col28	VARCHAR2(100 BYTE)
    Col29	VARCHAR2(2000 BYTE)
    Col30	VARCHAR2(100 BYTE)
    Col31	VARCHAR2(100 BYTE)
    Col32	VARCHAR2(100 BYTE)
    Col33	VARCHAR2(100 BYTE)
    Col34	VARCHAR2(100 BYTE)
    Col35	VARCHAR2(100 BYTE)
    Col36	VARCHAR2(100 BYTE)
    Col37	VARCHAR2(100 BYTE)
    Col38	DATE
    Col39	DATE
    Col40	DATE
    Col41	VARCHAR2(100 BYTE)
    Col42	VARCHAR2(100 BYTE)
    Col43	VARCHAR2(100 BYTE)
    Col44	VARCHAR2(100 BYTE)
    Col45	VARCHAR2(400 BYTE)
    Col46	VARCHAR2(400 BYTE)
    Col47	VARCHAR2(100 BYTE)
    Col48	VARCHAR2(100 BYTE)
    Col49	VARCHAR2(100 BYTE)
    Col50	VARCHAR2(100 BYTE)
    Col51	VARCHAR2(2500 BYTE)
    Col52	VARCHAR2(100 BYTE)
    Col53	VARCHAR2(100 BYTE)
    Col54	VARCHAR2(2500 BYTE)
    Col55	VARCHAR2(100 BYTE)
    Col56	VARCHAR2(100 BYTE)
    Col57	VARCHAR2(100 BYTE)
    Col58	VARCHAR2(100 BYTE)
    Col59	VARCHAR2(100 BYTE)
    Col60	VARCHAR2(100 BYTE)
    Col61	VARCHAR2(100 BYTE)
    Col62	VARCHAR2(100 BYTE)
    Col63	VARCHAR2(100 BYTE)
    Col64	VARCHAR2(100 BYTE)
    Col65	VARCHAR2(100 BYTE)
    Col66	VARCHAR2(100 BYTE)
    Col67	VARCHAR2(1000 BYTE)
    Col68	VARCHAR2(1000 BYTE)
    Col69	VARCHAR2(100 BYTE)
    La clef primaire est sur la col1 et la col69.

    Le plus étonnant c'est que j'ai fait une "expérience".
    Explication :
    La fonction de "remplissage" est appelée par un bouton (rien d'extraordinaire).
    Je lui fait remplir les 10 000 premiers éléments sachant que je fais un "SaveChanges" tous les 1000.
    A chaque "SaveChanges", la mémoire grimpe de 30 ~ 40 Mo.
    Nota bene : après chaque "SaveChanges" je "réinitialise grâce à un :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
     db.SaveChanges();
      i = 0;
    db.Dispose();
    db = new Entitees.Entities();
    GC.Collect();
    Et bien ça ne sert à rien, la mémoire baisse d'un iota.

    Là où ça devient surréaliste, c'est que une fois ce traitement terminé et que je reprend sur l'IHM, je relance la fonction sauvegarde_TAB (elle est suffisamment intelligente pour savoir où recommencer) pour charger les 10 000 suivants.
    La mémoire est toujours importante et puis à un moment, sans que j'explique pourquoi, lors d'un "SaveChanges", hop elle est nettoyée et rebaisse.

    Ce qui est encore plus étonnant c'est que si j'appelle la fonction sauvegarde_TAB avec une boucle for, et bien la mémoire ne baisse pas.

    Honnêtement, je ne comprends rien.
    Je pense qu'il s'agit d'un comportement imprévisible d'entity framework ou tu plugin Oracle. Bref du code codé en dur qu'on ne peut explorer.

    Ce qui me fait penser : y a t il une fonction C# pour "recharger" une DLL ?
    J'aimerais bien forcer le rechargement de la DLL d'entity framework et de celle d'Oracle pour voir.

  4. #4
    Membre à l'essai
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Août 2011
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2011
    Messages : 14
    Points : 24
    Points
    24
    Par défaut
    Bonsoir comme il a été dit plus haut ton code devrait fonctionner, je chargeais 200000 lignes voir plus de cette façon...
    Les fois ou j'ai pu avoir un OutOfMemory étaient liées à ma machine (les 4go était saturés) car j'effectuais les tests en local. De combien disposes tu de RAM?

    Tu es sure de ne pas avoir une boucle quelque part? D'un pure point de vue performance je t'invite aussi à regarder du coté du BulkCopy

  5. #5
    Nouveau membre du Club
    Inscrit en
    Novembre 2003
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Novembre 2003
    Messages : 29
    Points : 27
    Points
    27
    Par défaut
    Le fameux outofmemory a tendance a sortir vers les 800Mo chargés dans VS, quand a entity, il a un nombre d'objets limite chargés (cette limite est floue).
    Les prochaine versions de Vs et d'entity doivent corriger ces problemes.

  6. #6
    Membre émérite

    Homme Profil pro
    Software Developer
    Inscrit en
    Mars 2008
    Messages
    1 470
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Software Developer

    Informations forums :
    Inscription : Mars 2008
    Messages : 1 470
    Points : 2 368
    Points
    2 368
    Par défaut
    Utilises une boucle "foreach" au lieu de "for".
    Ensuite pour chaque élément tu instancie ta base de donnée, ajoute l'enregistrement, enregistre, puis termine la base.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
                    using (var db = new Entitees.Entities()) // Création que d'une instance au total
                    {
                        foreach (var item in lgec) // Simplification
                        {
                            db.TAB.AddObject(item); // Pas besoin de tout de suite sauvegarder
                        }
                        db.SaveChanges(); // Sauvagarde une seule fois
                    }
                }
    GC.Collect peut-etre utiliser avec des parametres, regardes MSDN.
    Mais tu n'as pas besoin d'appeller le garbage collector, Dispose et encore moins "new xxx" (tu crée une nouvelle référence mémoire), utiliser "using" suffit.

Discussions similaires

  1. Réponses: 2
    Dernier message: 28/02/2017, 10h35
  2. [Débutant] DataGridView et entity framework problème de rafraichissement
    Par xslert dans le forum Windows Forms
    Réponses: 5
    Dernier message: 18/04/2013, 12h11
  3. [Entity Framework] Problème de visualisation du schema relationnel
    Par Leelith dans le forum Entity Framework
    Réponses: 1
    Dernier message: 15/04/2010, 22h55
  4. Réponses: 2
    Dernier message: 01/03/2010, 21h32
  5. [Entity Framework] Problème avec le Designer
    Par farfadet dans le forum Framework .NET
    Réponses: 16
    Dernier message: 18/02/2010, 13h13

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo