Вальгринд обнаружил утечку

154

Все функции, упомянутые в этом блоке, являются библиотечными функциями. Как я могу исправить эту утечку памяти?

Он указан в категории « Все еще достижимо ». (Есть еще 4, которые очень похожи, но разных размеров)

 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)

Поймать: Как только я запустил мою программу, она не дала утечек памяти, но у нее была еще одна строка в выводе Valgrind, которой раньше не было:

Отказ от символов в 0x5296fa0-0x52af438 в /lib/libgcc_s-4.4.4-20100630.so.1 из-за munmap ()

Если утечка не может быть устранена, может кто-нибудь хотя бы объяснить, почему строка munmap () заставляет Valgrind сообщать 0 «все еще достижимых» утечек?

Редактировать:

Вот минимальный тестовый образец:

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

Бежать с:

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

источник
Valgrind FAQ .
jww

Ответы:

378

Существует несколько способов определения «утечки памяти». В частности, есть два основных определения «утечки памяти», которые широко используются программистами.

Первое обычно используемое определение «утечки памяти»: «Память была выделена и впоследствии не была освобождена до завершения программы». Тем не менее, многие программисты (справедливо) утверждают, что некоторые типы утечек памяти, которые соответствуют этому определению, на самом деле не создают никаких проблем, и поэтому не должны рассматриваться как настоящие «утечки памяти».

Возможно, более строгое (и более полезное) определение «утечки памяти» таково: «Память была выделена и впоследствии не может быть освобождена, поскольку у программы больше нет указателей на выделенный блок памяти». Другими словами, вы не можете освободить память, на которую у вас больше нет указателей. Поэтому такая память является «утечкой памяти». Valgrind использует это более строгое определение термина «утечка памяти». Это тип утечки, который потенциально может вызвать значительное истощение кучи, особенно для долгоживущих процессов.

Категория «все еще достижимая» в отчете об утечке Valgrind относится к выделениям, которые соответствуют только первому определению «утечки памяти». Эти блоки не были освобождены, но они могли быть освобождены (если бы программист захотел), потому что программа все еще отслеживала указатели на эти блоки памяти.

В общем, нет необходимости беспокоиться о «все еще доступных» блоках. Они не представляют собой проблему, которую могут вызвать настоящие утечки памяти. Например, обычно нет возможности исчерпать кучу из «все еще доступных» блоков. Это связано с тем, что эти блоки обычно являются одноразовыми, ссылки на которые хранятся в течение всего времени жизни процесса. Несмотря на то, что вы могли бы пройти и убедиться, что ваша программа освобождает всю выделенную память, это обычно не дает практической пользы, поскольку операционная система в любом случае восстановит всю память процесса после его завершения. Сравните это с правдой утечки памяти, которые, если оставить их незафиксированными, могут привести к тому, что процессу не хватит памяти, если он останется работать достаточно долго, или просто заставят процесс использовать гораздо больше памяти, чем необходимо.

Вероятно, единственный раз, когда полезно убедиться, что все распределения имеют совпадающие «освобождения», это если ваши инструменты обнаружения утечек не могут определить, какие блоки «все еще доступны» (но Valgrind может это сделать) или если ваша операционная система не восстанавливает все память завершающего процесса (все платформы, для которых Valgrind был перенесен для этого).

Дэн Молдинг
источник
Можете ли вы догадаться, что делает munmap (), что делает блоки "все еще достижимыми" исчезающими?
3
@crypto: это может быть munmapвызвано в результате выгрузки общего объекта. И все ресурсы, используемые общим объектом, могут быть освобождены до его выгрузки. Это может объяснить, почему «все еще достижимые» освобождаются в этом munmapделе. Я просто спекулирую здесь, хотя. Здесь недостаточно информации, чтобы сказать наверняка.
Дэн Молдинг
3
Один случай, когда «все еще достижимая» память может считаться утечкой памяти: предположим, у вас есть хеш-таблица, в которую вы добавляете указатели на выделенную память кучи в качестве значения. Если вы продолжаете вставлять новые записи в таблицу, но не удаляете и не освобождаете те, которые вам больше не нужны, он может расти бесконечно, что приводит к утечке памяти кучи, если эта память технически «все еще доступна». Это тот случай утечки памяти, которая может возникнуть в Java или других языках с мусором.
lvella
Смотрите также этот ответ в FAQ по Valgrind о «все еще достижимых» блоках, созданных STL. valgrind.org/docs/manual/faq.html#faq.reports
Джон Перри,
5
«многие программисты (справедливо) утверждают, что [утечка памяти] на самом деле не создает [а] проблемы и, следовательно, не должна рассматриваться как истинная утечка памяти» - Lol ... Создайте собственную DLL с такой утечкой памяти, а затем есть Java или .Net потреблять его. Java и .Net загружают и выгружают библиотеки DLL тысячи раз в течение жизни программы. Каждый раз, когда DLL перезагружается, она будет пропускать немного больше памяти. Долгосрочные программы в конечном итоге исчерпают память. Это сводит с ума сопровождающего OpenJDK Debian. То же самое он сказал в списке рассылки OpenSSL, когда мы обсуждали «доброкачественные» утечки памяти в OpenSSL.
jww
10

Поскольку внизу есть какая-то подпрограмма из семейства pthread (но я не знаю, какая именно), я думаю, что вы запустили какой-то поток как присоединяемый, который завершил выполнение.

Информация о состоянии выхода этого потока остается доступной, пока вы не позвоните pthread_join. Таким образом, память сохраняется в записи о потерях при завершении программы, но она все еще доступна, так как вы можете использовать pthread_joinдля доступа к ней.

Если этот анализ верен, либо запустите эти потоки отдельно, либо присоединитесь к ним до завершения вашей программы.

Изменить : я запустил вашу программу примера (после некоторых очевидных исправлений), и у меня нет ошибок, но следующее

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

Поскольку эта dl-вещь во многом напоминает то, что вы видите, я предполагаю, что вы видите известную проблему, для которой есть решение в виде файла подавления valgrind. Возможно, ваша система не обновлена, или ваш дистрибутив не поддерживает эти вещи. (Мой Ubuntu 10,4, 64-битный)

Йенс Гастедт
источник
Я получаю 0 ошибок, как и вы. Пожалуйста, ознакомьтесь с краткой информацией об утечках.
@crypto: я не понимаю Ты имеешь в виду, что у тебя такое же подавление, как и у меня?
Йенс Гастедт
used_suppression: 14 dl-hack3-cond-1 <- вот что я получаю
6

Вы, кажется, не понимаете, что still reachableзначит.

Все still reachableэто не утечка. Вам не нужно ничего делать с этим.

Занятый русский
источник
24
Это противоречит другим словам, предоставленным Valgrind, а также технически неверным. Память была «все еще доступна» при выходе из программы и, таким образом, потенциально утечка. Что если вы отлаживаете код для запуска в ОСРВ, которая не очищает память после выхода из программы?
Toymakerii
4
К сожалению, это не всегда так. Например, дескрипторы потерянных файлов могут считаться утечкой памяти, но valgrind классифицирует их как «все еще достижимые», предположительно потому, что указатели, ведущие к ним, все еще доступны в системной таблице. Но для отладки настоящий диагноз - «утечка памяти».
Голубой
Потерянные файловые дескрипторы не являются утечками памяти по определению. Может быть, вы говорите о потерянных FILEуказателей?
Занятый русский
6

Вот правильное объяснение «все еще достижимо»:

«По-прежнему достижимы» - утечки, назначенные глобальным и статически-локальным переменным. Поскольку valgrind отслеживает глобальные и статические переменные, он может исключать выделения памяти, которые назначаются «однажды и забудь». Глобальная переменная назначала распределение один раз и никогда не переназначала, что распределение обычно не является «утечкой» в том смысле, что оно не увеличивается бесконечно. В строгом смысле это все еще утечка, но обычно ее можно игнорировать, если только вы не педантичны.

Локальные переменные, которым назначены распределения, а не free'd, почти всегда имеют утечки.

Вот пример

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 будет сообщать о working_buf как «все еще достижимо - 16k», а temp_buf как «определенно потеряно - 5k».

дорога к абатству
источник
-1

Для будущих читателей «Все еще доступный» может означать, что вы забыли закрыть что-то вроде файла. Хотя в первоначальном вопросе это не так, вы всегда должны убедиться, что сделали это.

MonerosKin
источник