2009-05-27 6 views
0

J'essaie de détecter les fuites de mémoire en surchargeant globalement new et delete pour les builds de débogage et en maintenant une liste des blocs alloués. Cela fonctionne, mais signale incorrectement des fuites pour certains objets statiques. Par exemple, un vecteur statique std :: lui-même n'est pas alloué en utilisant new et delete, mais ses tampons internes le sont. Comme mon code de détection s'exécute avant le retour de la fonction principale, les destructeurs de ces objets ne sont pas encore appelés, ils n'ont donc pas encore supprimé leurs tampons et cela apparaît comme une "fuite".Comment traiter les objets statiques lors de la surcharge et de la suppression pour trouver des fuites de mémoire?

Une solution que j'ai trouvé est en supprimant et le placement newing chaque objet statique pour effacer leurs tampons avant de vérifier les fuites:

std::vector<int> f; 

f.~std::vector<int>(); 
new (&f) std::vector<int>(); 

C'est vraiment laid et bien y compris le code de nettoyage qui n'est pas requis semble mauvais. Existe-t-il un moyen d'exécuter mon code une fois que les statistiques ont été détruites? (même si c'est spécifique à Microsoft)

Répondre

2

Vous devez regarder dans std::allocator - le deuxième paramètre de modèle de presque tous les conteneurs STL.

+0

Comment cela aiderait-il? Je consignation les allocations, theres juste aucun moyen de faire le contrôle de fuite final après que les objets statiques ont été détruits – Lucas

+0

L'allocateur est en fait l'entité qui gère la mémoire pour les conteneurs. Implémentez votre allocateur de "détection de fuite" et donnez-le à l'instanciation du conteneur. Ne vous inquiétez pas pour les objets statiques eux-mêmes - ils sont alloués au moment de la compilation. –

+0

Oh je vois, je pourrais donner à tous les conteneurs statiques un autre allocateur qui les exclurait d'être suivis. Ouais cela fonctionnerait, mais cela exigerait que je change beaucoup de code partout dans mon système en raison du fait que l'allocateur est un paramètre de modèle. – Lucas

0

Vous pouvez essayer d'utiliser un gestionnaire atexit.

+0

Merci, je ne savais pas à propos de cette fonction. Alors que atexit ne fonctionnera pas pour moi puisque le code est dans une DLL, le _onexit spécifique à Microsoft le fera. Cependant, je l'ai juste essayé et malheureusement ma fonction s'exécute encore avant que les destructeurs statiques ne s'appellent – Lucas

1

Ecrivez une fonction qui vide votre liste de blocs alloués. Appelez-le après toute initialisation statique, mais avant que votre programme commence à allouer tout ce que vous voulez suivre. La première chose dans main() devrait le faire. Bien sûr, vous verrez toujours des fuites pour les vecteurs statiques qui ont été redimensionnés, donc vous pourriez vouloir les réserver plus gros pour commencer en mode de détection de fuites. Si un vecteur statique augmente inexorablement au fil du temps, alors vous voulez probablement savoir à ce sujet et le traiter comme une fuite, même si elle est statique.

Modifier en réponse à un commentaire:

Par exemple, au lieu d'un bit central de code pour détruire tous GLOBALS au moment de l'arrêt:

template<typename T> 
class Reserve { 
    Reserve(T &t, typename T::size_type size) { 
     t.reserve(size); 
    } 
}; 

static std::vector<int> foo; 
static Reserve<std::vector<int> > foo_r(foo, 50); 

static std:string bar; 
static Reserve<std::string> bar_r(bar, 256); 

Il pourrait y avoir quelque chose que vous pouvez faire avec un modèle de fonction , de sorte que l'inférence de type se débarrasse de la nécessité de répéter le type. Et pourquoi pas:

template<typename T> 
int reserve(T &t, typename T::size_type size) { 
    t.reserve(size); 
    return 0; 
} 

static std::vector<int> foo; 
static int foo_r = reserve(foo, 50); 

static std:string bar; 
static int bar_r = reserve(bar, 256); 
+0

Ouais cela semble une approche raisonnable, mais je ne pense pas avoir à réserver manuellement tous mes vecteurs statiques est beaucoup plus propre que de faire le in-place delete/new thing on exit – Lucas

+0

Vrai, bien que le code puisse être placé à côté de l'objet à réserver, au lieu d'avoir besoin d'une "liste magique de tous les globaux". Effacer la liste d'allocation présente cependant d'autres avantages. Si vous ne supprimez pas vos globals, vous pouvez vider une liste utile d'allocations à d'autres moments que la sortie. Vous pouvez également effacer/vider l'un ou l'autre côté d'une routine particulière au lieu du programme entier. –

1

Placez tout votre code de détection de fuite dans un autre DLL séparé. Assurez-vous ensuite que le code que vous souhaitez vérifier pour les fuites dépend de la DLL. Cela permet au chargeur de s'assurer que la "DLL de vérification des fuites" est initialisée avant tout le reste et qu'elle est déchargée après tout le reste. Cela permettra de s'assurer que votre code de vérification des fuites fonctionne après que tous les autres codes ont été déchargés (et les destructeurs appelés).

C'est la technique que j'ai utilisée lorsque j'ai créé Visual Leak Detector, et cela semble bien fonctionner.

+0

C'est une super idée à laquelle je n'avais pas pensé mais malheureusement ma situation est compliquée car il y a plusieurs DLL dans mon système, chacune avec sa propre instance du traqueur de fuite. Cela signifierait que la DLL obtiendrait juste la référence comptée au lieu de maintenir l'état séparé – Lucas

+0

La solution simple à cela est de maintenir l'état de vérification de fuite pour chaque module (DLL ou exécutable) ensemble (par exemple utiliser une carte std :: map indiquer à la poignée du module à laquelle elle appartient). –

Questions connexes