Publicité
+ Répondre à la discussion
Affichage des résultats 1 à 6 sur 6
  1. #1
    Invité régulier
    Inscrit en
    novembre 2007
    Messages
    28
    Détails du profil
    Informations forums :
    Inscription : novembre 2007
    Messages : 28
    Points : 5
    Points
    5

    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 :
    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 éprouvé
    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 : 459
    Points
    459

    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
    Invité régulier
    Inscrit en
    novembre 2007
    Messages
    28
    Détails du profil
    Informations forums :
    Inscription : novembre 2007
    Messages : 28
    Points : 5
    Points
    5

    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 :
    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 :
    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
    Futur Membre du Club
    Homme Profil pro Ange Guyader
    Développeur .NET
    Inscrit en
    août 2011
    Messages
    14
    Détails du profil
    Informations personnelles :
    Nom : Homme Ange Guyader
    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 : 17
    Points
    17

    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
    Invité de passage
    Inscrit en
    novembre 2003
    Messages
    17
    Détails du profil
    Informations forums :
    Inscription : novembre 2003
    Messages : 17
    Points : 4
    Points
    4

    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 Expert

    Homme Profil pro Gilles Vino
    Software Developer
    Inscrit en
    mars 2008
    Messages
    1 475
    Détails du profil
    Informations personnelles :
    Nom : Homme Gilles Vino
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Software Developer

    Informations forums :
    Inscription : mars 2008
    Messages : 1 475
    Points : 2 373
    Points
    2 373

    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 :
    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.

Liens sociaux

Règles de messages

  • Vous ne pouvez pas créer de nouvelles discussions
  • Vous ne pouvez pas envoyer des réponses
  • Vous ne pouvez pas envoyer des pièces jointes
  • Vous ne pouvez pas modifier vos messages
  •