Fuite toujours accessible détectée par Valgrind

154

Toutes les fonctions mentionnées dans ce bloc sont des fonctions de bibliothèque. Comment puis-je rectifier cette fuite de mémoire?

Il est répertorié dans la catégorie " Toujours joignable ". (Il y en a 4 autres, qui sont très similaires, mais de tailles variables)

 630 bytes in 1 blocks are still reachable in loss record 5 of 5
    at 0x4004F1B: calloc (vg_replace_malloc.c:418)
    by 0x931CD2: _dl_new_object (dl-object.c:52)
    by 0x92DD36: _dl_map_object_from_fd (dl-load.c:972)
    by 0x92EFB6: _dl_map_object (dl-load.c:2251)
    by 0x939F1B: dl_open_worker (dl-open.c:255)
    by 0x935965: _dl_catch_error (dl-error.c:178)
    by 0x9399C5: _dl_open (dl-open.c:584)
    by 0xA64E31: do_dlopen (dl-libc.c:86)
    by 0x935965: _dl_catch_error (dl-error.c:178)
    by 0xA64FF4: __libc_dlopen_mode (dl-libc.c:47)
    by 0xAE6086: pthread_cancel_init (unwind-forcedunwind.c:53)
    by 0xAE61FC: _Unwind_ForcedUnwind (unwind-forcedunwind.c:126)

Catch: Une fois que j'ai exécuté mon programme, il n'a donné aucune fuite de mémoire, mais il y avait une ligne supplémentaire dans la sortie Valgrind, qui n'était pas présente auparavant:

Suppression des syms à 0x5296fa0-0x52af438 dans /lib/libgcc_s-4.4.4-20100630.so.1 en raison de munmap ()

Si la fuite ne peut pas être corrigée, quelqu'un peut-il au moins expliquer pourquoi la ligne munmap () amène Valgrind à signaler 0 fuites «encore accessibles»?

Éditer:

Voici un échantillon de test minimal:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

void *runner(void *param) {
    /* some operations ... */
    pthread_exit(NULL);
}

int n;

int main(void) {

    int i;
    pthread_t *threadIdArray;

    n=10; /* for example */

    threadIdArray = malloc((n+n-1)*sizeof(pthread_t));  

    for(i=0;i<(n+n-1);i++) {
        if( pthread_create(&threadIdArray[i],NULL,runner,NULL) != 0 ) {
            printf("Couldn't create thread %d\n",i);
            exit(1);
        }
    }


    for(i=0;i<(n+n-1);i++) {
        pthread_join(threadIdArray[i],NULL);
    }

    free(threadIdArray);

    return(0);
}

Courir avec:

valgrind -v --leak-check=full --show-reachable=yes ./a.out

la source
FAQ Valgrind .
jww

Réponses:

378

Il existe plusieurs façons de définir la «fuite de mémoire». En particulier, il existe deux définitions principales de «fuite de mémoire» qui sont couramment utilisées par les programmeurs.

La première définition couramment utilisée de «fuite de mémoire» est «La mémoire a été allouée et n'a pas été libérée par la suite avant la fin du programme». Cependant, de nombreux programmeurs soutiennent (à juste titre) que certains types de fuites de mémoire qui correspondent à cette définition ne posent en fait aucun problème et ne devraient donc pas être considérés comme de véritables «fuites de mémoire».

Une définition sans doute plus stricte (et plus utile) de la «fuite de mémoire» est: «La mémoire a été allouée et ne peut pas être libérée par la suite parce que le programme n'a plus de pointeurs vers le bloc de mémoire alloué». En d'autres termes, vous ne pouvez pas libérer de la mémoire sur laquelle vous n'avez plus de pointeurs. Une telle mémoire est donc une "fuite de mémoire". Valgrind utilise cette définition plus stricte du terme «fuite de mémoire». C'est le type de fuite qui peut potentiellement provoquer un épuisement important du tas, en particulier pour les processus de longue durée.

La catégorie «encore accessible» dans le rapport de fuite de Valgrind fait référence aux allocations qui correspondent uniquement à la première définition de «fuite de mémoire». Ces blocs n'ont pas été libérés, mais ils auraient pu l'être (si le programmeur l'avait voulu) car le programme gardait toujours la trace des pointeurs vers ces blocs de mémoire.

En général, il n'y a pas lieu de s'inquiéter des blocs "encore accessibles". Ils ne posent pas le genre de problème que peuvent causer de véritables fuites de mémoire. Par exemple, il n'y a normalement pas de potentiel d'épuisement du tas à partir de blocs «encore accessibles». En effet, ces blocs sont généralement des allocations ponctuelles, dont les références sont conservées pendant toute la durée de vie du processus. Bien que vous puissiez vous assurer que votre programme libère toute la mémoire allouée, il n'y a généralement aucun avantage pratique à le faire puisque le système d'exploitation récupérera toute la mémoire du processus après la fin du processus, de toute façon. Comparez cela avec le vrai des fuites de mémoire qui, si elles ne sont pas corrigées, peuvent entraîner un manque de mémoire dans un processus s'il est laissé en cours d'exécution assez longtemps, ou entraîneront simplement une consommation de mémoire beaucoup plus importante que nécessaire.

Probablement le seul moment où il est utile de s'assurer que toutes les allocations ont des "libérations" correspondantes est si vos outils de détection de fuite ne peuvent pas dire quels blocs sont "encore accessibles" (mais Valgrind peut le faire) ou si votre système d'exploitation ne récupère pas tout la mémoire d'un processus de fin (toutes les plates-formes sur lesquelles Valgrind a été porté pour cela).

Moulage Dan
la source
pouvez-vous supposer ce que fait le munmap () qui fait disparaître les blocs "encore accessibles"?
3
@crypto: il se peut qu'il munmapsoit appelé à la suite du déchargement d'un objet partagé. Et toutes les ressources utilisées par l'objet partagé peuvent être libérées avant son déchargement. Cela pourrait expliquer pourquoi les "encore joignables" sont libérés dans l' munmapaffaire. Je spécule juste ici, cependant. Il n'y a pas assez d'informations ici pour être sûr.
Dan Moulding
3
Un cas où la mémoire «encore accessible» peut être considérée comme une fuite de mémoire: supposons que vous ayez une table de hachage où vous ajoutez des pointeurs vers la mémoire allouée au tas comme valeur. Si vous continuez à insérer de nouvelles entrées sur la table, mais que vous ne supprimez pas et ne libérez pas celles dont vous n'avez plus besoin, cela peut croître indéfiniment, laissant échapper un événement de mémoire de tas si cette mémoire est thecniquement "toujours accessible". C'est le cas d'une fuite de mémoire que vous pouvez avoir en Java ou dans d'autres langages de récupération de place.
lvella
Voir aussi cette réponse dans la FAQ de valgrind sur les blocs "encore accessibles" qui sont créés par STL. valgrind.org/docs/manual/faq.html#faq.reports
John Perry
5
"De nombreux programmeurs (à juste titre) soutiennent que [la mémoire perdue] ne pose pas réellement [un] problème, et ne devrait donc pas être considérée comme de véritables fuites de mémoire" - Lol ... Construisez une DLL native avec ce genre de fuite de mémoire, puis faites en sorte que Java ou .Net le consomme. Java et .Net chargent et déchargent des DLL des milliers de fois au cours de la vie d'un programme. Chaque fois que la DLL est rechargée, elle perdra un peu plus de mémoire. Les programmes de longue durée finiront par manquer de mémoire. Cela rend fou le responsable d'OpenJDK de Debian. Il a dit la même chose sur la liste de diffusion OpenSSL pendant que nous discutions des fuites de mémoire "bénignes" d'OpenSSL.
jww
10

Puisqu'il y a une routine de la famille pthread en bas (mais je ne connais pas celle-là), je suppose que vous avez lancé un thread comme joignable qui a mis fin à l'exécution.

Les informations sur l'état de sortie de ce thread restent disponibles jusqu'à ce que vous appeliez pthread_join. Ainsi, la mémoire est conservée dans un enregistrement de perte à la fin du programme, mais elle est toujours accessible car vous pouvez l'utiliser pthread_joinpour y accéder.

Si cette analyse est correcte, lancez ces threads détachés ou rejoignez-les avant de terminer votre programme.

Edit : J'ai exécuté votre exemple de programme (après quelques corrections évidentes) et je n'ai pas d'erreurs mais les suivantes

==18933== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)
--18933-- 
--18933-- used_suppression:      2 dl-hack3-cond-1
--18933-- used_suppression:      2 glibc-2.5.x-on-SUSE-10.2-(PPC)-2a

Puisque la dl-chose ressemble beaucoup à ce que vous voyez, je suppose que vous voyez un problème connu qui a une solution en termes de fichier de suppression valgrind. Peut-être que votre système n'est pas à jour ou que votre distribution ne gère pas ces éléments. (Le mien est ubuntu 10.4, 64 bits)

Jens Gustedt
la source
Je reçois 0 erreurs comme vous. Veuillez consulter le résumé des fuites pour obtenir des informations sur les «fuites».
@crypto: Je ne comprends pas. Vous voulez dire que vous avez les mêmes suppressions que moi?
Jens Gustedt
used_suppression: 14 dl-hack3-cond-1 <- c'est ce que j'obtiens
6

Vous ne semblez pas comprendre ce que cela still reachablesignifie.

Tout still reachablen'est pas une fuite. Vous n'avez rien à faire.

Employé russe
la source
24
Cela est en conflit avec les autres verbages fournis par Valgrind et techniquement incorrect. La mémoire était «toujours accessible» à la sortie du programme et donc potentiellement une fuite. Que faire si vous déboguez du code à exécuter sur un RTOS qui ne nettoie pas bien la mémoire après la sortie du programme?
Toymakerii
4
Malheureusement, ce n'est pas toujours vrai. Les descripteurs de fichiers perdus, par exemple, peuvent compter comme des fuites de mémoire, mais valgrind les classe comme «toujours accessibles», probablement parce que les pointeurs qui y mènent sont toujours accessibles dans une table système. Mais aux fins de débogage, le vrai diagnostic est une "fuite de mémoire".
Cyan
Les descripteurs de fichiers perdus ne sont pas des fuites de mémoire par définition. Vous parlez peut-être de FILEpointeurs perdus ?
Employé russe le
6

Voici une explication correcte de «toujours accessible»:

Les fuites attribuées aux variables globales et statiques-locales sont «toujours accessibles». Parce que valgrind suit les variables globales et statiques, il peut exclure les allocations de mémoire qui sont assignées «une fois et oublie». Une variable globale affectée une allocation une fois et jamais réaffectée cette allocation n'est généralement pas une «fuite» dans le sens où elle ne croît pas indéfiniment. C'est toujours une fuite au sens strict, mais peut généralement être ignorée à moins que vous ne soyez pédant.

Les variables locales auxquelles des allocations sont attribuées et non libres sont presque toujours des fuites.

Voici un exemple

int foo(void)
{
    static char *working_buf = NULL;
    char *temp_buf;
    if (!working_buf) {
         working_buf = (char *) malloc(16 * 1024);
    }
    temp_buf = (char *) malloc(5 * 1024);

    ....
    ....
    ....

}

Valgrind rapportera working_buf comme "toujours accessible - 16k" et temp_buf comme "définitivement perdu - 5k".

Abbey Road
la source
-1

Pour les futurs lecteurs, "Toujours joignable" peut signifier que vous avez oublié de fermer quelque chose comme un fichier. Bien que cela ne semble pas être le cas dans la question initiale, vous devez toujours vous assurer de l'avoir fait.

MonerosKin
la source