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

C++ Discussion :

Surcharge de new / delete et Memory Manager


Sujet :

C++

  1. #1
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 923
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 923
    Points : 220 590
    Points
    220 590
    Billets dans le blog
    128
    Par défaut Surcharge de new / delete et Memory Manager
    Bonjour à tous,

    Tout d'abord, j'ai suivi l'excellent tutoriel suivant: http://loulou.developpez.com/tutorie.../partie1/#L2.2
    Dans le tutoriel, pour ceux qui ne veulent pas lire, on apprend comment surcharger new / new[] / delete / delete[] et surtout comment traquer toutes les allocations ainsi que les désallocations.

    J'ai fait un code semblable, mais pourtant, moi, à la destruction du MemoryManager, il appelle mon delete surchargé, ce qui fait que, du coup, comme memory (ma std::map) est détruite, et que c'est elle même qui appelle un delete... ça crashe dans ma fonction release().

    ... Je ne comprends bien sur pas pourquoi, et j'aimerai éviter ce crash (logique). L'histoire, c'est, que comme les surcharges des opérateurs ne sont pas visible dans le MemoryManager, pourquoi ai je cette appel à release()?

    J'attache mon code en pièce jointes (projet VS2010) pour que cela soit plus facile à voir que dans le forum.

    Merci pour votre aide.
    Fichiers attachés Fichiers attachés

  2. #2
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 923
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 923
    Points : 220 590
    Points
    220 590
    Billets dans le blog
    128
    Par défaut
    Réponse: Bah, le std::map fait un new que je capture avec mon memory manager lors de la destruction de ce memory manager.
    Du coup, bah ça fait très mal, la moitié des objets détruits ... mais ... on essaie de les réutiliser.

    Solution: J'ai réimplémenté une list (avec clés) moi même, utilisant les malloc() / free() pour le moment. Cela marche correctement (jusqu'à présent) même si j'ai mis pas mal de temps à mettre en place la solution.

  3. #3
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    Réponse: Bah, le std::map fait un new que je capture avec mon memory manager lors de la destruction de ce memory manager.
    Du coup, bah ça fait très mal, la moitié des objets détruits ... mais ... on essaie de les réutiliser.

    Solution: J'ai réimplémenté une list (avec clés) moi même, utilisant les malloc() / free() pour le moment. Cela marche correctement (jusqu'à présent) même si j'ai mis pas mal de temps à mettre en place la solution.
    J'avais commencé à regarder un peu ton code, et j'avais essayé de vider la map dans le destructeur de memorymanager. Mais le problème reste même dans ce cas. J'avoue que je n'ai pas continué à creuser, mais cela m'a intrigué. Reste-t-il des allocations dans la map y compris si celle ci est vide (je ne me souvient plus si j'ai essayé un std::swap(memory, temp) avec temp sur la pile.
    De plus, je n'ai pas vu de différence flagrante avec la version de Laurent Gomilla et je me suis demandé s'il avait le même problème ?

  4. #4
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 923
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 923
    Points : 220 590
    Points
    220 590
    Billets dans le blog
    128
    Par défaut
    De plus, je n'ai pas vu de différence flagrante avec la version de Laurent Gomilla et je me suis demandé s'il avait le même problème ?
    J'en ai parlé directement avec Laurent Gomilla certes en MP ... ce qui n'est pas une très bonne solution :s ... mais bon.
    Il n'y a pas de différence dans le sens, pas de différence dans l'implémentation (juste quelques fonctionnalités en plus, et des trucs (la signature) que j'ai tiré d'un bouquin sur le problème)
    Après, mon hypothèse (que j'avais exposé à Laurent Gomilla) était que lui, il avait fait le programme en 2004 / 2005 si je me rappelle bien, et que entre temps, nous pouvons avoir une implémentation de la std::map qui change (surtout lorsque j'utilise VS2010)
    Après je peux montrer le callstack ou cela crash, mais je crois que vous avez retracé vous aussi le problème.
    Après, il m'a aussi conseillé que ce que je voulais faire était ultra dangereux, et que ça résultait souvent à de nombreux problèmes (comme j'ai pu le remarquer )
    Après, dans mon essai... même si c'était vide, ça plantait toujours ... à cause que semble t'il, l'implémentation utilise un système de node ... enfin, pour le peu que j'ai compris.

    L'autre point qui ma aussi un peu perdu, c'est que malgré le #undef de delete à la fin du main et la séparation des implémentation new / delete du MemoryManager, il tente tout de même d'utiliser mon delete perso, et ça, il ne me semblait pas que cela devait arriver ...

  5. #5
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    Après, mon hypothèse (que j'avais exposé à Laurent Gomilla) était que lui, il avait fait le programme en 2004 / 2005 si je me rappelle bien, et que entre temps, nous pouvons avoir une implémentation de la std::map qui change (surtout lorsque j'utilise VS2010)
    Fortement possible.
    Citation Envoyé par LittleWhite Voir le message
    Après je peux montrer le callstack ou cela crash, mais je crois que vous avez retracé vous aussi le problème.
    effectivement, j'ai regardé déjà.
    Citation Envoyé par LittleWhite Voir le message
    Après, il m'a aussi conseillé que ce que je voulais faire était ultra dangereux, et que ça résultait souvent à de nombreux problèmes (comme j'ai pu le remarquer )
    Quels sont les points risqués ?

    Citation Envoyé par LittleWhite Voir le message
    L'autre point qui ma aussi un peu perdu, c'est que malgré le #undef de delete à la fin du main et la séparation des implémentation new / delete du MemoryManager, il tente tout de même d'utiliser mon delete perso, et ça, il ne me semblait pas que cela devait arriver ...
    Je pense que cela doit venir de ces 2 définitions :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    inline void operator delete(void* pPointer)
    {
        MemoryManager::get().release(pPointer, false);
    }
     
    inline void operator delete[](void* pPointer)
    {
        MemoryManager::get().release(pPointer, true);
    }
    Le remplacement doit dépasser la durée de vie de ta variable manager ?

    et cela dans le delete :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
        if ( itMemoryBlock == memory.end() )
        {
            // Can be if the pointer was not allocated with this memory manager...
    #ifdef _VERBOSE_DEBUGGING
        #ifdef _FILELOGGING
            logFile << "Memory Manager -> free (ERROR: Pointer not found in the manager)" << std::endl;
        #else
            std::cout << "Memory Manager -> free (ERROR: Pointer not found in the manager)" << std::endl;
        #endif
    #endif
            // Anyway, we have to free it
            free(pPointer);
            return;
        }
    Cela me parait très bizarre (pour ne pas dire plus) de vouloir détruire coûte que coûte un pointeur que tu ne gères pas. D'ailleurs, si je mets en commentaire la ligne avec free, je n'ai plus d'erreur de plantage.

    En fait, j'ai l'impression que c'est rapidement un gros 'b....el' car l'utilisation de la STL et donc d'allocations/destructions implicites dans tes fonctions ... d'allocation/libération peut rapidement amener à des appels récursifs. Peut être était-ce de cela dont Laurent voulait parler en disant que c'était une démarche risquée ?

  6. #6
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 923
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 923
    Points : 220 590
    Points
    220 590
    Billets dans le blog
    128
    Par défaut
    Quels sont les points risqués ?
    J'ai fait une boulette, mais je ne crois pas que cela avait été précisé.
    Bien sur, le premier problème que l'on peut cité, c'est celui que je rapporte ici .
    Sinon, il y a un gros risque que certaines bibliothèques externes se fasse aussi piéger par le MemoryManager. Là dessus, ce ne sont que mes propres hypothèses (un peu naïves)

    Le remplacement doit dépasser la durée de vie de ta variable manager ?
    Oui maintenant, j'en suis assez certain (et je ne dis pas cela que parce que j'ai eu le bug)
    C'est un remplacement fait au niveau du compilateur ... du coup ...

    Cela me parait très bizarre (pour ne pas dire plus) de vouloir détruire coûte que coûte un pointeur que tu ne gères pas. D'ailleurs, si je mets en commentaire la ligne avec free, je n'ai plus d'erreur de plantage.
    Bien sur, il n'y a plus plantage. De plus, je ne veux pas détruire ce pointeur (moi perso non)
    Mince ... je viens de comprendre le sous entendu ... vous marquez un point

    Dans le tuto de Laurent Gomilla il est dit le suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    // Si le bloc n'a pas été alloué, on génère une erreur
        if (It == m_Blocks.end())
        {
            // En fait ça arrive souvent, du fait que le delete surchargé
            // est pris en compte même là où on n'inclue pas DebugNew.h,
            // mais pas la macro pour le new
            // Dans ce cas on détruit le bloc et on quitte immédiatement
            free(Ptr);
            return;
        }
    Mais il est vrai ... que l'on pourrait tout simplement faire un return ...

    En fait, j'ai l'impression que c'est rapidement un gros 'b....el' car l'utilisation de la STL et donc d'allocations/destructions implicites dans tes fonctions ... d'allocation/libération peut rapidement amener à des appels récursifs. Peut être était-ce de cela dont Laurent voulait parler en disant que c'était une démarche risquée ?
    On peut aussi revenir sur le fait que si j'enlève le free() ... je fais une fuite de mémoire explicite, mais dans le code de la STL ...

    Mais mais, lorsque j'y repense ... l'erreur n'étais pas là ou vous le dite, chez moi ...
    elle était car la variable 'memory' était à zero.
    [edit]
    J'ai vérifié ... l'erreur est ici chez moi:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    // Finding the block related to this pointer
    MemoryMap::iterator itMemoryBlock = memory.find(pPointer);
    Mais sinon, oui, le fait d'enlever le free() ... enlève le crash O_o (je m'y perd).
    Par contre ... comme mon astucieux std::cout l'indique dans le get du singleton, le memorymanager est toujours appelé alors qu'il ne devrait pas. Ceci, j'ai réussi à l'éviter avec la version ou je défini mes propres structures de données (pour remplacer la std::map)

    J'imagine que les problèmes ne font que commencer car cela semble plus du hacking de code qu'autre chose, de vouloir faire un tel MemoryManager.
    [/edit]

  7. #7
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    J'ai vérifié ... l'erreur est ici chez moi:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    // Finding the block related to this pointer
    MemoryMap::iterator itMemoryBlock = memory.find(pPointer);
    A partir du moment où tu libères un pointeur que tu n'as pas alloué, tu ne sais pas si et ce que tu as corrompu. En plus l'erreur sur le map a lieu après la destruction du memory manager.
    Je pense que tu devrait avoir un new de même vie que ton delete, ce qui n'est pas le cas il me semble actuellement. Et je me demande si l'utilisation de la STL (string/flux) dans new/delete ne risque pas de poser des problèmes à cause d'allocations à l'intérieur de leur implémentation

  8. #8
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 923
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 923
    Points : 220 590
    Points
    220 590
    Billets dans le blog
    128
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    A partir du moment où tu libères un pointeur que tu n'as pas alloué, tu ne sais pas si et ce que tu as corrompu. En plus l'erreur sur le map a lieu après la destruction du memory manager.
    Moi je voyais plus comme suit:
    J'ai une fonction qui est appelé pour la liberation, donc si je ne libère pas la mémoire, elle ne fait pas son rôle. La moitié du temps c'est l'utilisateur qui appelle plus ou moins directement cette fonction. Si l'utilisateur passe un pointeur invalide, je dois faire comme dans un vrai programme ... et crasher (ou faire crasher le free) ... car l'utilisateur a fait une betise ...

    Je pense que tu devrait avoir un new de même vie que ton delete, ce qui n'est pas le cas il me semble actuellement. Et je me demande si l'utilisation de la STL (string/flux) dans new/delete ne risque pas de poser des problèmes à cause d'allocations à l'intérieur de leur implémentation
    Ce serait bien mais comment (avoir le new de même vie que le delete)?

  9. #9
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    Moi je voyais plus comme suit:
    J'ai une fonction qui est appelé pour la liberation, donc si je ne libère pas la mémoire, elle ne fait pas son rôle. La moitié du temps c'est l'utilisateur qui appelle plus ou moins directement cette fonction. Si l'utilisateur passe un pointeur invalide, je dois faire comme dans un vrai programme ... et crasher (ou faire crasher le free) ... car l'utilisateur a fait une betise ...
    Mais l'adresse peut très bien être valide mais allouée différemment. A mon avis, c'est en fonction de ça :
    Ce serait bien mais comment (avoir le new de même vie que le delete)?
    A priori, oui. Sinon, tu risque de faire des delete qui ne correspondent pas à un new géré par ton memory manager.

  10. #10
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 923
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 923
    Points : 220 590
    Points
    220 590
    Billets dans le blog
    128
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    A priori, oui. Sinon, tu risque de faire des delete qui ne correspondent pas à un new géré par ton memory manager.
    Euh ma question était du genre ... comment pourrai-je ? car là, je manque un peu d'idée... (même si vous pouviez juste m'orienter sur une solution, cela ne me gêne pas)

  11. #11
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    Euh ma question était du genre ... comment pourrai-je ? car là, je manque un peu d'idée... (même si vous pouviez juste m'orienter sur une solution, cela ne me gêne pas)
    Ben comme pour le delete . Tu définis ta propre version de l'opérateur avec les signatures que tu veux surcharger.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void* operator new(std::size_t size) /*throw(std::bad_alloc)*/;
    void* operator new(std::size_t size, const std::nothrow_t&) /*throw()*/;
    void operator delete(void* ptr) /*throw()*/;
    void operator delete(void* ptr, const std::nothrow_t&) throw();
    void* operator new[](std::size_t size) /*throw(std::bad_alloc)*/;
    void* operator new[](std::size_t size, const std::nothrow_t&) /*throw()*/;
    void operator delete[](void* ptr) /*throw()*/;
    void operator delete[](void* ptr, const std::nothrow_t&) /*throw()*/;
    Le problème va être l'interférence avec le #define new utilisé pour ajouter le fichier/ligne. J'ai pas ton code sous les yeux, mais je me demande s'il ne faut pas séparer les surcharges des opérateurs (new et delete) de ta macro et le fichier .h correspondant.

  12. #12
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 923
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 923
    Points : 220 590
    Points
    220 590
    Billets dans le blog
    128
    Par défaut
    Merci pour votre réponse.

    Je viens de lire le livre: "Effective C++ Third Edition" de Scott Meyers et je dois dire que c'est un très bon livre (malgré qu'il ne couvre pas toutes mes interrogations (en même temps le livre qui le ferait serait énorme)

    Bref, il y a une partie (3 items) sur la surcharge des allocations de mémoire.

    Déjà, je peux dire que mon implémentation du new est hors norme (non conforme donc) car il manque:
    - Le cas ou on passe une taille de 0 (oui c'est une allocation valide en C++, et il faut allouer au moins 1 byte ... car elle doit renvoyer un pointeur valide)
    - La boucle qui essaie d'allouer plusieurs fois (soit avec un appel sur le callback (std::new_handler), soit lancer une exception.
    - Mon delete devrait géré silencieusement le cas du pointeur NULL.


    Après avoir lu tout cela, je suis un peu loin d'être dans la bonne implémentation . (et on ne parle pas du thread safe, de la memory alignment et autres).

    Mais, je pensais à votre solution, de surcharger le new() global (car j'ai compris que mon code trichait, mais ne pouvait complètement tricher sur la partie du delete), je crois que ce serai une très mauvaise idée, car j'aurais surement un appel à new durant l'allocation de mon memory manager (donc, je reviendrai à un cas inverse de celui pour lequel je suis venu).

    Pour conclure ce message, je dois dire que la meilleure méthode est de crée une classe qui réimplémente new et delete. Ce qui permettrait de laisser ces implémentations à un niveau "local". Après, il faut voir les possibilités d'héritage de cette classe (héritage, que je voudrais garder que dans le mode DEBUG). Ou alors, une deuxième voix à suivre est de jouer avec des macro qui définissent les surcharges (mais là..., je suis un peu moins fort sur ce poins)

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 18
    Dernier message: 07/10/2010, 03h18
  2. intrigue sur la surcharge du new et delete
    Par swirtel dans le forum C++
    Réponses: 12
    Dernier message: 07/09/2006, 16h23
  3. [Débutant]Constructeur et new/delete
    Par Geolem dans le forum C++
    Réponses: 5
    Dernier message: 02/12/2005, 22h11
  4. Namespace et surcharge operator new/delete
    Par ZeLegolas dans le forum C++
    Réponses: 11
    Dernier message: 26/07/2005, 14h55

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