Vazamento ainda acessível detectado por Valgrind

154

Todas as funções mencionadas neste bloco são funções de biblioteca. Como posso corrigir esse vazamento de memória?

Ele está listado na categoria " Ainda acessível ". (Existem mais 4, que são muito semelhantes, mas de tamanhos variados)

 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)

Captura: Uma vez que executei meu programa, não houve vazamentos de memória, mas havia uma linha adicional na saída Valgrind, que não estava presente antes:

Descartar syms em 0x5296fa0-0x52af438 em /lib/libgcc_s-4.4.4-20100630.so.1 devido ao munmap ()

Se o vazamento não puder ser corrigido, alguém pode pelo menos explicar por que a linha munmap () faz com que Valgrind relate 0 vazamentos "ainda acessíveis"?

Editar:

Aqui está uma amostra mínima de teste:

#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);
}

Correr com:

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

fonte

Respostas:

378

Há mais de uma maneira de definir "vazamento de memória". Em particular, existem duas definições principais de "vazamento de memória" que são comuns entre os programadores.

A primeira definição comumente usada de "vazamento de memória" é "Memória foi alocada e não foi liberada posteriormente antes do término do programa". No entanto, muitos programadores (com razão) argumentam que certos tipos de vazamentos de memória que se encaixam nessa definição não representam nenhum tipo de problema e, portanto, não devem ser considerados verdadeiros "vazamentos de memória".

Uma definição discutivelmente mais rígida (e mais útil) de "vazamento de memória" é: "A memória foi alocada e não pode ser liberada posteriormente porque o programa não possui mais ponteiros para o bloco de memória alocado". Em outras palavras, você não pode liberar memória para a qual não possui mais ponteiros. Essa memória é, portanto, um "vazamento de memória". Valgrind usa essa definição mais rigorosa do termo "vazamento de memória". Esse é o tipo de vazamento que pode causar um esgotamento significativo do heap, especialmente para processos de longa duração.

A categoria "ainda acessível" no relatório de vazamento da Valgrind refere-se a alocações que se encaixam apenas na primeira definição de "vazamento de memória". Esses blocos não foram liberados, mas poderiam ter sido liberados (se o programador quisesse) porque o programa ainda estava acompanhando os ponteiros para esses blocos de memória.

Em geral, não há necessidade de se preocupar com blocos "ainda acessíveis". Eles não representam o tipo de problema que os verdadeiros vazamentos de memória podem causar. Por exemplo, normalmente não há potencial para esgotamento de heap de blocos "ainda acessíveis". Isso ocorre porque esses blocos geralmente são alocações únicas, cujas referências são mantidas durante toda a vida útil do processo. Embora você possa verificar e garantir que seu programa libere toda a memória alocada, geralmente não há nenhum benefício prático, pois o sistema operacional recuperará toda a memória do processo após o término do processo. Compare isso com true vazamentos de memória que, se deixados sem correção, podem causar falta de memória em um processo, se deixados em execução por tempo suficiente ou simplesmente causar um processo que consome muito mais memória do que o necessário.

Provavelmente, o único momento em que é útil garantir que todas as alocações tenham "liberações" correspondentes é que suas ferramentas de detecção de vazamentos não conseguem dizer quais blocos "ainda são alcançáveis" (mas a Valgrind pode fazer isso) ou se o sistema operacional não recupera todas as a memória de um processo de término (todas as plataformas para as quais o Valgrind foi portado para isso).

Dan Moulding
fonte
você pode imaginar o que o munmap () está fazendo que faz desaparecer os blocos "ainda acessíveis"?
3
@ cripto: Pode ser que munmapseja invocado como resultado do descarregamento de um objeto compartilhado. E todos os recursos usados ​​pelo objeto compartilhado podem estar sendo liberados antes de serem descarregados. Isso poderia explicar por que os "ainda acessíveis" estão sendo liberados no munmapcaso. Só estou especulando aqui. Não há informações suficientes aqui para ter certeza.
Dan Molding
3
Um caso em que a memória "ainda acessível" pode ser considerada um vazamento de memória: suponha que você tenha uma tabela de hash na qual adiciona ponteiros para acumular a memória alocada como valor. Se você continuar inserindo novas entradas na tabela, mas não remover e liberar as que não precisa mais, ele poderá crescer indefinidamente, causando um evento de memória heap com vazamento se essa memória ainda estiver "acessível". É o caso de vazamento de memória que você pode ter em Java ou em outras linguagens de coleta de lixo.
lvella
Veja também esta resposta na FAQ do valgrind sobre blocos "ainda acessíveis" que são criados pelo STL. valgrind.org/docs/manual/faq.html#faq.reports
John Perry
5
"muitos programadores (com razão) argumentam que [a memória vazada] na verdade não representa um problema e, portanto, não devem ser considerados verdadeiros vazamentos de memória" - Lol ... Crie uma DLL nativa com esse tipo de vazamento de memória e depois ter Java ou .Net consumi-lo. Java e .Net carregam e descarregam DLLs milhares de vezes durante a vida de um programa. Cada vez que a DLL é recarregada, ela vaza um pouco mais de memória. Programas de longa duração acabarão ficando sem memória. Isso deixa o mantenedor do OpenJDK do Debian louco. Ele disse o mesmo na lista de discussão do OpenSSL enquanto discutíamos os vazamentos de memória "benignos" do OpenSSL.
JWW
10

Como existe uma rotina da família pthread na parte inferior (mas eu não conheço essa em particular), meu palpite seria que você lançou algum encadeamento como junção que encerrou a execução.

As informações do estado de saída desse encadeamento são mantidas disponíveis até você ligar pthread_join. Portanto, a memória é mantida em um registro de perda na finalização do programa, mas ainda é acessível, pois você pode usá pthread_join-la para acessá-la.

Se essa análise estiver correta, inicie esses encadeamentos desanexados ou junte-se a eles antes de encerrar seu programa.

Editar : executei seu programa de amostra (após algumas correções óbvias) e não tenho erros, mas o seguinte

==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

Como a dl-coisa se parece muito com o que você vê, acho que você vê um problema conhecido que tem uma solução em termos de arquivo de supressão valgrind. Talvez seu sistema não esteja atualizado ou sua distribuição não mantenha essas coisas. (O meu é o ubuntu 10.4, 64bit)

Jens Gustedt
fonte
Estou recebendo 0 erros como você. Por favor, verifique o resumo do vazamento para obter informações sobre os "vazamentos".
@ cripto: eu não entendo. Quer dizer que você tem as mesmas supressões que eu?
Jens Gustedt 5/10/10
used_suppression: 14 dl-hack3-cond-1 <- é o que eu recebo #
6

Você não parece entender o que still reachablesignifica.

Qualquer coisa nãostill reachable é um vazamento. Você não precisa fazer nada sobre isso.

Russo empregado
fonte
24
Isso entra em conflito com o outro verbage fornecido pelo Valgrind, além de tecnicamente incorreto. A memória estava "ainda acessível" na saída do programa e, portanto, potencialmente um vazamento. E se você estivesse depurando código para executar em um RTOS que não limpa bem a memória após a saída do programa?
Toymakerii
4
Infelizmente, isso nem sempre é verdade. Os descritores de arquivo perdido, por exemplo, podem contar como vazamento de memória, mas o valgrind os classifica como "ainda alcançáveis", presumivelmente porque os ponteiros que os levam ainda estão acessíveis em uma tabela do sistema. Mas, para fins de depuração, o diagnóstico real é um "vazamento de memória".
Cyan
Descritores de arquivos perdidos não são vazamentos de memória por definição. Talvez você esteja falando de FILEindicadores perdidos ?
Empregado Russian
6

Aqui está uma explicação adequada de "ainda acessível":

"Ainda acessível" são vazamentos atribuídos a variáveis ​​globais e estáticas-locais. Como o valgrind rastreia variáveis ​​globais e estáticas, ele pode excluir alocações de memória designadas como "uma vez e esquecer". Uma variável global atribuiu uma alocação uma vez e nunca reatribuiu essa alocação normalmente não é um "vazamento" no sentido de que não cresce indefinidamente. Ainda é um vazamento no sentido estrito, mas geralmente pode ser ignorado, a menos que você seja pedante.

Variáveis ​​locais às quais são atribuídas alocações e não liberadas quase sempre são vazamentos.

Aqui está um exemplo

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 relatará working_buf como "ainda acessível - 16k" e temp_buf como "definitivamente perdido - 5k".

Abbey Road
fonte
-1

Para futuros leitores, "Ainda acessível" pode significar que você esqueceu de fechar algo como um arquivo. Embora isso não pareça assim na pergunta original, você deve sempre garantir que fez isso.

MonerosKin
fonte