Досі доступний витік виявлений Valgrind


155

Усі функції, згадані в цьому блоці, - це функції бібліотеки. Як я можу виправити цей витік пам'яті?

Він перерахований під категорією " Ще доступний ". (Є ще 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, якого раніше не було:

Відхилення syms у 0x5296fa0-0x52af438 у /lib/libgcc_s-4.4.4-20100630.so.1 завдяки munmap ()

Якщо витік не вдається усунути, може хтось принаймні пояснити, чому рядок мутапи () змушує Valgrind повідомити про "ще доступні" витоки?

Редагувати:

Ось мінімальний тестовий зразок:

#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

Відповіді:


379

Існує більше одного способу визначення "витоку пам'яті". Зокрема, є два основні визначення "витоку пам'яті", які часто використовуються серед програмістів.

Перше поширене визначення поняття "витік пам'яті" полягає в тому, що "Пам'ять була виділена і згодом не була звільнена до завершення програми". Однак багато програмістів (справедливо) стверджують, що певні типи витоку пам'яті, що відповідають цьому визначенню, насправді не представляють жодної проблеми, і тому їх не слід вважати справжніми "витоками пам'яті".

Напевно, суворіше (і корисніше) визначення поняття "витік пам'яті" полягає в тому, що "Пам'ять була розподілена і не може бути згодом звільнена, оскільки програма більше не має покажчиків на виділений блок пам'яті". Іншими словами, ви не можете звільнити пам'ять, на яку у вас більше немає вказівників. Отже, така пам'ять є "витоком пам'яті". Valgrind використовує це суворіше визначення терміна "витік пам'яті". Це тип витоку, який потенційно може спричинити значне виснаження купи, особливо для тривалих процесів.

Категорія "все ще доступна" у звіті про витік Valgrind стосується асигнувань, які відповідають лише першому визначенню "витік пам'яті". Ці блоки не були звільнені, але вони могли бути звільнені (якби програміст хотів), оскільки програма все ще відслідковувала покажчики на ці блоки пам'яті.

Взагалі, не потрібно турбуватися про "ще доступні" блоки. Вони не створюють такої проблеми, яку можуть викликати справжні витоки пам'яті. Наприклад, зазвичай не існує потенціалу для виснаження купи від "ще доступних" блоків. Це пояснюється тим, що ці блоки зазвичай є одноразовими виділеннями, посилання на які зберігаються протягом усієї тривалості процесу. Хоча ви могли пройти і переконатися, що програма звільняє всю виділену пам'ять, від цього практично немає користі, оскільки операційна система все одно поверне всю пам'ять процесу після закінчення процесу. Порівняйте це з правдою протікання пам'яті, які, якщо їх не виправлено, можуть призвести до того, що процес буде вичерпано, якщо воно буде працювати досить довго, або просто призведе до того, що процес буде споживати набагато більше пам'яті, ніж потрібно.

Напевно, єдиний час, коли корисно переконатись у тому, що всі розподіли мають відповідні "вільні", якщо ваші інструменти виявлення витоку не можуть визначити, які блоки "ще доступні" (але це може зробити Valgrind) або якщо ваша операційна система не відшкодує всі пам'ять процесу завершення (всі платформи, для яких Valgrind перенесені для цього).


чи можете ви припустити, що робить мунпак (), що змушує блоки "все ще доступні" зникати?

3
@crypto: можливо, що munmapвикликається в результаті вивантаження спільного об'єкта. І всі ресурси, що використовуються спільним об'єктом, можуть бути звільнені до його завантаження. Це може пояснити, чому "ще доступні речі" звільняються у munmapсправі. Я просто спекулюю тут. Тут недостатньо інформації, щоб точно сказати.
Dan Molding

3
Один випадок, коли "все ще доступна" пам'ять може вважатися витоком пам'яті: припустимо, у вас є хеш-таблиця, де ви додаєте покажчики на купу виділеної пам'яті як значення. Якщо ви продовжуєте вставляти нові записи в стіл, але не видалите та звільнить тих, які вам більше не потрібні, він може зростати нескінченно довго, витікаючи події купи пам'яті, якщо ця пам’ять є теоретично "все ще доступною". Це випадок витоку пам'яті, який ви можете мати на Java або інших зібраних сміттях.
lvella

Дивіться також цю відповідь у поширених FAQ про "ще доступні" блоки, створені STL. valgrind.org/docs/manual/faq.html#faq.reports
Джон Перрі

5
"багато програмістів (справедливо) стверджують, що [просочена пам'ять] насправді не представляє [a] проблеми, і тому не слід вважати справжніми витоками пам'яті" - Lol ... Створіть рідну DLL з таким видом пам'яті, а потім є Java або .Net споживають його. Java та .Net завантажують та завантажують DLL тисячі разів протягом життя програми. Кожен раз, коли DLL буде перезавантажено, вона просочиться трохи більше пам’яті. Довго працюючі програми з часом втратять пам’ять. Це зводить з розуму технічного обслуговування Debian OpenJDK. Він сказав це те ж саме у списку розсилки 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, 64bit)


Я отримую 0 помилок, як і ви. Будь ласка, перегляньте підсумок витоку, щоб отримати інформацію про "витоки".

@crypto: Я не розумію. Ви маєте на увазі, що у вас такі ж пригнічення, як у мене?
Jens Gustedt

used_suppression: 14 dl-hack3-cond-1 <- ось що я отримую

6

Ви, здається, не розумієте, що still reachable означає.

Все still reachableце НЕ витік. Вам не потрібно нічого з цим робити.


24
Це суперечить іншому суперечтю, наданому Valgrind, а також технічно неправильним. Пам'ять була "досяжна" при виході з програми і, таким чином, потенційно витікала. Що робити, якщо ви налагоджували код для запуску на RTOS, який не добре очищає пам'ять після виходу з програми?
Toymakerii

4
На жаль, це не завжди так. Дескриптори втрачених файлів, наприклад, можуть вважатись витоком пам'яті, але valgrind класифікує їх як "ще доступні", імовірно, тому, що покажчики, що ведуть до них, все ще доступні в системній таблиці. Але з метою налагодження справжній діагноз - це "витік пам'яті".
Cyan

Дескриптори втрачених файлів не є витоком пам'яті за визначенням. Можливо, ви говорите про втрачені FILEпокажчики?
працевлаштований росіянин

6

Ось правильне пояснення "ще доступного":

"Ще доступні" - це витоки, присвоєні глобальним та статично-локальним змінним. Оскільки valgrind відстежує глобальні та статичні змінні, він може виключати розподіли пам'яті, які присвоюються "раз і забудь". Глобальна змінна призначила розподіл один раз і ніколи не призначила це виділення, як правило, не є "витоком" у тому сенсі, що воно не зростає на невизначений термін. Це все ще витік у строгому розумінні, але зазвичай його можна ігнорувати, якщо ви не педантичні.

Локальні змінні, яким призначені асигнування та не вільні, майже завжди є витоками.

Ось приклад

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

Для майбутніх читачів "Все ще доступні" можуть означати, що ви забули закрити щось на зразок файлу. Хоча в початковому запитанні це не виглядає так, ви завжди повинні бути впевнені, що зробили це.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.