Kebocoran yang Dapat Dicapai masih terdeteksi oleh Valgrind

154

Semua fungsi yang disebutkan dalam blok ini adalah fungsi perpustakaan. Bagaimana saya bisa memperbaiki kebocoran memori ini?

Itu terdaftar di bawah kategori " Masih terjangkau ". (Ada 4 lebih, yang sangat mirip, tetapi dengan berbagai ukuran)

 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: Setelah saya menjalankan program saya, tidak ada kebocoran memori, tetapi ia memiliki satu baris tambahan dalam output Valgrind, yang sebelumnya tidak ada:

Membuang syms di 0x5296fa0-0x52af438 di /lib/libgcc_s-4.4.4-20100630.so.1 karena munmap ()

Jika kebocoran tidak dapat diperbaiki, bisakah seseorang setidaknya menjelaskan mengapa munmap () line menyebabkan Valgrind melaporkan 0 kebocoran yang "masih dapat dicapai"?

Edit:

Berikut ini contoh uji 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);
}

Jalankan dengan:

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

sumber
Valgrind FAQ .
jww

Jawaban:

378

Ada lebih dari satu cara untuk mendefinisikan "kebocoran memori". Secara khusus, ada dua definisi utama "kebocoran memori" yang umum digunakan di kalangan programmer.

Definisi "kebocoran memori" yang umum digunakan pertama adalah, "Memori dialokasikan dan tidak kemudian dibebaskan sebelum program dihentikan." Namun, banyak programmer (benar) berpendapat bahwa tipe tertentu dari kebocoran memori yang sesuai dengan definisi ini tidak benar-benar menimbulkan masalah, dan karenanya tidak boleh dianggap sebagai "kebocoran memori" yang sebenarnya.

Definisi yang lebih kuat (dan lebih bermanfaat) dari "kebocoran memori" adalah, "Memori dialokasikan dan tidak dapat kemudian dibebaskan karena program tidak lagi memiliki pointer ke blok memori yang dialokasikan." Dengan kata lain, Anda tidak dapat membebaskan memori yang tidak lagi memiliki petunjuk. Memori semacam itu karenanya merupakan "kebocoran memori". Valgrind menggunakan definisi yang lebih ketat tentang istilah "kebocoran memori". Ini adalah jenis kebocoran yang berpotensi menyebabkan penumpukan tumpukan yang signifikan, terutama untuk proses yang berumur panjang.

Kategori "masih dapat dijangkau" dalam laporan kebocoran Valgrind mengacu pada alokasi yang hanya sesuai dengan definisi pertama "kebocoran memori". Blok-blok ini tidak dibebaskan, tetapi mereka dapat dibebaskan (jika programmer ingin) karena program masih melacak pointer ke blok memori tersebut.

Secara umum, tidak perlu khawatir tentang blok "masih dapat dijangkau". Mereka tidak menimbulkan masalah yang bisa disebabkan oleh kebocoran memori yang sebenarnya . Misalnya, biasanya tidak ada potensi untuk tumpukan kehabisan dari blok "masih dapat dijangkau". Ini karena blok ini biasanya alokasi satu kali, referensi yang disimpan sepanjang durasi proses. Meskipun Anda dapat melewati dan memastikan bahwa program Anda membebaskan semua memori yang dialokasikan, biasanya tidak ada manfaat praktis dari melakukannya karena sistem operasi akan mendapatkan kembali semua memori proses setelah proses berakhir. Bandingkan ini dengan true kebocoran memori yang, jika dibiarkan tidak tetap, dapat menyebabkan proses kehabisan memori jika dibiarkan berjalan cukup lama, atau hanya akan menyebabkan proses untuk mengkonsumsi memori jauh lebih banyak daripada yang diperlukan.

Mungkin satu-satunya waktu yang berguna untuk memastikan bahwa semua alokasi memiliki pencocokan "membebaskan" adalah jika alat deteksi kebocoran Anda tidak dapat mengetahui blok mana yang "masih dapat dijangkau" (tetapi Valgrind dapat melakukan ini) atau jika sistem operasi Anda tidak mengklaim kembali semua memori proses terminating (semua platform yang telah diporting Valgrind untuk melakukan ini).

Dan Moulding
sumber
dapatkah Anda menduga apa yang dilakukan munmap () yang membuat blok "masih dapat dijangkau" hilang?
3
@crypto: Bisa jadi itu munmapdipanggil sebagai akibat dari membongkar objek yang dibagikan. Dan semua sumber daya yang digunakan oleh objek yang dibagikan mungkin dibebaskan sebelum dibongkar. Ini bisa menjelaskan mengapa "masih terjangkau" dibebaskan dalam munmapkasus ini. Saya hanya berspekulasi di sini. Tidak ada informasi yang cukup di sini untuk mengatakan dengan pasti.
Dan Moulding
3
Satu kasus di mana "masih dapat dijangkau" memori dapat dianggap sebagai kebocoran memori: anggap Anda memiliki tabel hash di mana Anda menambahkan pointer ke tumpukan memori yang dialokasikan sebagai nilai. Jika Anda terus memasukkan entri baru di atas meja, tetapi tidak akan menghapus dan membebaskan yang tidak Anda butuhkan lagi, itu dapat tumbuh tanpa batas waktu, membocorkan peristiwa memori tumpukan jika memori itu secara teknis "masih dapat dijangkau". Ini adalah kasus kebocoran memori yang dapat Anda miliki di Jawa atau bahasa sampah lainnya yang dikumpulkan.
lvella
Lihat juga jawaban ini di FAQ valgrind tentang blok "masih dapat dijangkau" yang dibuat oleh STL. valgrind.org/docs/manual/faq.html#faq.reports
John Perry
5
"banyak programmer (benar) berpendapat bahwa [memori yang bocor] tidak benar-benar menimbulkan masalah [a], dan oleh karena itu tidak boleh dianggap kebocoran memori yang benar" - Lol ... Membangun DLL asli dengan kebocoran memori semacam itu, dan kemudian minta Java atau .Net mengkonsumsinya. Java dan .Net memuat dan membongkar DLL ribuan kali selama masa pakai suatu program. Setiap kali DLL dimuat, itu akan membocorkan sedikit lebih banyak memori. Program yang berjalan lama pada akhirnya akan kehabisan memori. Ini membuat pengelola OpenJDK Debian marah. Dia mengatakan hal yang sama pada milis OpenSSL ketika kami mendiskusikan kebocoran memori "jinak" OpenSSL.
jww
10

Karena ada beberapa rutin dari keluarga pthread di bagian bawah (tapi saya tidak tahu itu), tebakan saya adalah bahwa Anda telah meluncurkan beberapa utas sebagai joinable yang telah menghentikan eksekusi.

Informasi status keluar utas itu tetap tersedia sampai Anda menelepon pthread_join. Dengan demikian, memori disimpan dalam catatan kerugian pada saat penghentian program, tetapi masih dapat dijangkau karena Anda dapat menggunakannya pthread_joinuntuk mengaksesnya.

Jika analisis ini benar, luncurkan utas ini secara terpisah, atau bergabunglah sebelum menghentikan program Anda.

Sunting : Saya menjalankan program sampel Anda (setelah beberapa koreksi yang jelas) dan saya tidak memiliki kesalahan selain yang berikut

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

Karena dl-hal itu menyerupai banyak dari apa yang Anda lihat, saya kira Anda melihat masalah yang diketahui yang memiliki solusi dalam hal file penekan valgrind. Mungkin sistem Anda tidak mutakhir, atau distribusi Anda tidak mempertahankan hal-hal ini. (Milik saya adalah ubuntu 10.4, 64bit)

Jens Gustedt
sumber
Saya mendapatkan 0 kesalahan sama seperti Anda. Silakan periksa ringkasan kebocoran untuk info tentang "kebocoran".
@crypto: Saya tidak mengerti. Anda berarti Anda memiliki penindasan yang sama seperti yang saya miliki?
Jens Gustedt
used_suppression: 14 dl-hack3-cond-1 <- itulah yang saya dapatkan
6

Anda tampaknya tidak mengerti apa still reachableartinya.

Apa pun still reachableitu bukan kebocoran. Anda tidak perlu melakukan apa-apa tentang itu.

Mempekerjakan bahasa Rusia
sumber
24
Ini bertentangan dengan verbage lain yang disediakan oleh Valgrind dan juga secara teknis salah. Memori itu "masih dapat dijangkau" pada saat keluar dari program sehingga berpotensi bocor. Bagaimana jika Anda men-debug kode untuk dijalankan pada RTOS yang tidak membersihkan memori dengan baik setelah program keluar?
Toymakerii
4
Sayangnya, itu tidak selalu benar. Deskriptor File Hilang misalnya dapat dihitung sebagai kebocoran memori, tetapi valgrind mengklasifikasikannya sebagai "masih dapat dijangkau", mungkin karena pointer yang mengarah ke mereka masih dapat diakses dalam tabel sistem. Tetapi untuk tujuan debugging, diagnosis sebenarnya adalah "kebocoran memori".
Cyan
Deskriptor file yang hilang bukan kebocoran memori menurut definisi. Mungkin Anda berbicara tentang FILEpointer hilang ?
Bekerja bahasa Rusia
6

Berikut ini penjelasan yang tepat tentang "masih bisa dijangkau":

"Masih dapat dijangkau" adalah kebocoran yang ditetapkan untuk variabel global dan statis-lokal. Karena valgrind melacak variabel global dan statis, ia dapat mengecualikan alokasi memori yang ditetapkan "sekali-dan-lupakan". Variabel global menetapkan alokasi sekali dan tidak pernah ditugaskan kembali bahwa alokasi biasanya bukan "kebocoran" dalam arti bahwa itu tidak tumbuh tanpa batas. Ini masih merupakan kebocoran dalam arti yang ketat, tetapi biasanya dapat diabaikan kecuali jika Anda jago.

Variabel lokal yang diberi alokasi dan tidak bebas hampir selalu bocor.

Berikut ini sebuah contoh

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 akan melaporkan working_buf sebagai "masih dapat dijangkau - 16k" dan temp_buf sebagai "pasti hilang - 5k".

Abbey Road
sumber
-1

Untuk pembaca masa depan, "Still Reachable" mungkin berarti Anda lupa menutup sesuatu seperti file. Meskipun tampaknya tidak seperti itu dalam pertanyaan awal, Anda harus selalu memastikan bahwa Anda telah melakukannya.

MonerosKin
sumber