Fuga aún accesible detectada por Valgrind

154

Todas las funciones mencionadas en este bloque son funciones de biblioteca. ¿Cómo puedo rectificar esta pérdida de memoria?

Se incluye en la categoría " Aún accesible ". (Hay 4 más, que son muy similares, pero de diferentes tamaños)

 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: una vez que ejecuté mi programa, no dio pérdidas de memoria, pero tenía una línea adicional en la salida de Valgrind, que no estaba presente antes:

Descartando syms en 0x5296fa0-0x52af438 en /lib/libgcc_s-4.4.4-20100630.so.1 debido a munmap ()

Si la fuga no se puede rectificar, ¿puede alguien explicar al menos por qué la línea munmap () hace que Valgrind informe 0 fugas "aún accesibles"?

Editar:

Aquí hay una muestra de prueba mínima:

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

Corre con:

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

fuente

Respuestas:

378

Hay más de una forma de definir "pérdida de memoria". En particular, hay dos definiciones principales de "pérdida de memoria" que son de uso común entre los programadores.

La primera definición comúnmente utilizada de "pérdida de memoria" es: "La memoria se asignó y no se liberó posteriormente antes de que el programa terminara". Sin embargo, muchos programadores (con razón) argumentan que ciertos tipos de pérdidas de memoria que se ajustan a esta definición en realidad no plantean ningún tipo de problema y, por lo tanto, no deben considerarse verdaderas "pérdidas de memoria".

Una definición posiblemente más estricta (y más útil) de "pérdida de memoria" es: "La memoria se asignó y no se puede liberar posteriormente porque el programa ya no tiene ningún puntero al bloque de memoria asignado". En otras palabras, no puede liberar memoria a la que ya no tiene punteros. Tal memoria es, por lo tanto, una "pérdida de memoria". Valgrind utiliza esta definición más estricta del término "pérdida de memoria". Este es el tipo de fuga que potencialmente puede causar un agotamiento significativo en el montón, especialmente para procesos de larga duración.

La categoría "todavía accesible" dentro del informe de pérdida de Valgrind se refiere a asignaciones que se ajustan solo a la primera definición de "pérdida de memoria". Estos bloques no se liberaron, pero podrían haberse liberado (si el programador hubiera querido hacerlo) porque el programa seguía haciendo un seguimiento de los punteros a esos bloques de memoria.

En general, no hay necesidad de preocuparse por los bloques "aún accesibles". No plantean el tipo de problema que pueden causar las verdaderas pérdidas de memoria. Por ejemplo, normalmente no hay potencial para el agotamiento del montón de bloques "aún accesibles". Esto se debe a que estos bloques suelen ser asignaciones de una sola vez, cuyas referencias se mantienen durante toda la vida útil del proceso. Si bien puede continuar y asegurarse de que su programa libere toda la memoria asignada, por lo general no hay ningún beneficio práctico al hacerlo, ya que el sistema operativo recuperará toda la memoria del proceso después de que el proceso finalice, de todos modos. Contrasta esto con verdad pérdidas de memoria que, si no se corrigen, podrían hacer que un proceso se quede sin memoria si se ejecuta el tiempo suficiente, o simplemente hará que un proceso consuma mucha más memoria de la necesaria.

Probablemente, el único momento en que es útil asegurarse de que todas las asignaciones tengan "liberaciones" coincidentes es si sus herramientas de detección de fugas no pueden determinar qué bloques son "aún accesibles" (pero Valgrind puede hacer esto) o si su sistema operativo no reclama todo la memoria de un proceso final (todas las plataformas que Valgrind ha sido portado para hacer esto).

Dan Molding
fuente
¿puedes suponer qué está haciendo el munmap () que hace que desaparezcan los bloques "aún accesibles"?
3
@crypto: podría ser que munmapse invoque como resultado de descargar un objeto compartido. Y todos los recursos utilizados por el objeto compartido podrían liberarse antes de que se descargue. Esto podría explicar por qué los "todavía alcanzables" se están liberando en el munmapcaso. Sin embargo, solo estoy especulando aquí. No hay suficiente información aquí para decir con certeza.
Dan Moulding
3
Un caso en el que la memoria "aún accesible" se puede considerar una pérdida de memoria: suponga que tiene una tabla hash donde agrega punteros a la memoria asignada del montón como valor. Si sigue insertando nuevas entradas en la tabla, pero no quita y libera las que ya no necesita, puede crecer indefinidamente, perdiendo el evento de memoria de montón si esa memoria es técnicamente "aún accesible". Este es el caso de pérdida de memoria que puede tener en Java u otros lenguajes recolectados de basura.
lvella
Consulte también esta respuesta en las preguntas frecuentes de valgrind sobre los bloques "aún accesibles" creados por STL. valgrind.org/docs/manual/faq.html#faq.reports
John Perry
55
"muchos programadores (con razón) argumentan que [la memoria perdida] en realidad no plantea [un] problema, y ​​por lo tanto no debe considerarse verdaderas pérdidas de memoria" - Jajaja ... Construya una DLL nativa con ese tipo de pérdida de memoria, y luego hacer que Java o .Net lo consuman. Java y .Net cargan y descargan archivos DLL miles de veces durante la vida de un programa. Cada vez que se vuelve a cargar la DLL, perderá un poco más de memoria. Los programas de larga ejecución eventualmente se quedarán sin memoria. Enloquece al mantenedor de OpenJDK de Debian. Dijo lo mismo en la lista de correo de OpenSSL mientras discutíamos las pérdidas de memoria "benignas" de OpenSSL.
jww
10

Dado que hay una rutina de la familia pthread en la parte inferior (pero no sé cuál en particular), supongo que ha lanzado un hilo como unible que ha terminado la ejecución.

La información del estado de salida de ese hilo se mantiene disponible hasta que llame pthread_join. Por lo tanto, la memoria se mantiene en un registro de pérdida al finalizar el programa, pero aún se puede pthread_joinacceder a ella, ya que podría usarla para acceder a ella.

Si este análisis es correcto, inicie estos hilos separados o únase a ellos antes de finalizar su programa.

Editar : ejecuté su programa de muestra (después de algunas correcciones obvias) y no tengo errores, pero los siguientes

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

Dado que la dl-cosa se parece mucho a lo que ves, supongo que ves un problema conocido que tiene una solución en términos de un archivo de supresión valgrind. Quizás su sistema no esté actualizado o su distribución no mantenga estas cosas. (El mío es ubuntu 10.4, 64 bits)

Jens Gustedt
fuente
Recibo 0 errores como tú. Consulte el resumen de fugas para obtener información sobre las "fugas".
@ crypto: No entiendo. ¿Quieres decir que tienes las mismas supresiones que yo?
Jens Gustedt
used_suppression: 14 dl-hack3-cond-1 <- eso es lo que obtengo
6

No pareces entender lo que still reachablesignifica.

Cualquier cosa nostill reachable es una fuga. No necesitas hacer nada al respecto.

Ruso empleado
fuente
24
Esto entra en conflicto con la otra información proporcionada por Valgrind y es técnicamente incorrecta. La memoria era "todavía accesible" a la salida del programa y, por lo tanto, potencialmente una fuga. ¿Qué pasaría si estuviera depurando código para ejecutarlo en un RTOS que no limpia bien la memoria después de salir del programa?
Toymakerii
44
Desafortunadamente, eso no siempre es cierto. Los descriptores de archivos perdidos, por ejemplo, pueden contar como una pérdida de memoria, pero valgrind los clasifica como "aún accesibles", presumiblemente porque los punteros que los conducen todavía son accesibles dentro de una tabla del sistema. Pero con el propósito de depurar, el diagnóstico real es una "pérdida de memoria".
Cian
Los descriptores de archivos perdidos no son pérdidas de memoria por definición. Tal vez estás hablando de FILEpunteros perdidos ?
Empleado ruso
6

Aquí hay una explicación adecuada de "todavía accesible":

"Aún accesible" son fugas asignadas a variables globales y estáticas locales. Debido a que valgrind rastrea las variables globales y estáticas, puede excluir las asignaciones de memoria que se asignan "una vez y olvidar". Una variable global asignó una asignación una vez y nunca reasignó que la asignación no suele ser una "fuga" en el sentido de que no crece indefinidamente. Sigue siendo una fuga en sentido estricto, pero generalmente puede ignorarse a menos que sea pedante.

Las variables locales a las que se asignan asignaciones y no se liberan casi siempre son fugas.

Aquí hay un ejemplo

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 informará working_buf como "todavía accesible - 16k" y temp_buf como "definitivamente perdido - 5k".

Abbey Road
fuente
-1

Para futuros lectores, "Aún accesible" podría significar que olvidó cerrar algo como un archivo. Si bien no parece ser así en la pregunta original, siempre debes asegurarte de haberlo hecho.

MonerosKin
fuente