Bagaimana menemukan kebocoran memori dalam kode / proyek C ++?


180

Saya seorang programmer C ++ pada platform Windows. Saya menggunakan Visual Studio 2008.

Saya biasanya berakhir di kode dengan kebocoran memori.

Biasanya saya menemukan kebocoran memori dengan memeriksa kode, tetapi rumit dan tidak selalu merupakan pendekatan yang baik.

Karena saya tidak mampu membeli alat pendeteksi kebocoran memori berbayar, saya ingin kalian menyarankan cara terbaik untuk menghindari kebocoran memori.

  1. Saya ingin tahu bagaimana programmer dapat menemukan kebocoran memori.
  2. Apakah ada standar atau prosedur yang harus diikuti untuk memastikan tidak ada kebocoran memori dalam program?

29
"Aku biasanya berakhir dengan kode dengan kebocoran memori." Jika Anda menggunakan variabel otomatis, wadah, dan pointer pintar (dan ikuti praktik terbaik untuk menggunakan pointer pintar), kebocoran memori harus sangat jarang terjadi. Ingat, dalam hampir semua kasus, Anda harus menggunakan manajemen sumber daya otomatis .
James McNellis


1
@Hostile Fork: "bagaimana seseorang menghindari biasanya berakhir dengan kode dengan kebocoran memori" tidak tercakup oleh jawaban-jawaban itu.
Doc Brown

2
@ Doc Brown: Tidak merasa ingin melihat itu juga, tapi semua dibahas di tempat lain, seperti stackoverflow.com/questions/45627/...
HostileFork mengatakan jangan percaya SE

1
Detektor Kebocoran DIY: Anda bisa meletakkan kode yang dicurigai dalam loop tak terbatas dan kemudian membuka task manager, biasanya bahkan kebocoran kecil akan mengisi memori dalam hitungan detik atau menit (Itu tergantung pada kompleksitas kode Anda dan CPU Anda). Jika itu tidak terjadi, potongan kode itu mungkin tidak bocor.
Hello World

Jawaban:


270

Instruksi

Hal yang Anda Butuhkan

  • Kemahiran dalam C ++
  • Kompiler C ++
  • Debugger dan alat perangkat lunak investigasi lainnya

1

Pahami dasar-dasar operator. Operator C ++ newmengalokasikan memori tumpukan. The deletemembebaskan Operator tumpukan memori. Untuk setiap new, Anda harus menggunakan deletesehingga Anda membebaskan memori yang sama yang Anda alokasikan:

char* str = new char [30]; // Allocate 30 bytes to house a string.

delete [] str; // Clear those 30 bytes and make str point nowhere.

2

Alokasikan kembali memori hanya jika Anda telah menghapus. Dalam kode di bawah ini, strdapatkan alamat baru dengan alokasi kedua. Alamat pertama hilang tanpa bisa diperbaiki, dan begitu juga 30 byte yang ditunjuknya. Sekarang tidak mungkin dibebaskan, dan Anda mengalami kebocoran memori:

char* str = new char [30]; // Give str a memory address.

// delete [] str; // Remove the first comment marking in this line to correct.

str = new char [60]; /* Give str another memory address with
                                                    the first one gone forever.*/

delete [] str; // This deletes the 60 bytes, not just the first 30.

3

Lihat tugas penunjuk itu. Setiap variabel dinamis (memori yang dialokasikan pada heap) perlu dikaitkan dengan pointer. Ketika variabel dinamis menjadi terlepas dari penunjuknya, menjadi tidak mungkin untuk dihapus. Sekali lagi, ini menghasilkan kebocoran memori:

char* str1 = new char [30];

char* str2 = new char [40];

strcpy(str1, "Memory leak");

str2 = str1; // Bad! Now the 40 bytes are impossible to free.

delete [] str2; // This deletes the 30 bytes.

delete [] str1; // Possible access violation. What a disaster!

4

Hati-hati dengan petunjuk lokal. Pointer yang Anda nyatakan dalam suatu fungsi dialokasikan pada stack, tetapi variabel dinamis yang ditunjuknya dialokasikan pada heap. Jika Anda tidak menghapusnya, itu akan tetap ada setelah program keluar dari fungsi:

void Leak(int x){

char* p = new char [x];

// delete [] p; // Remove the first comment marking to correct.

}

5

Perhatikan kurung kurawal setelah "hapus." Gunakan deletedengan sendirinya untuk membebaskan satu objek. Gunakan delete []dengan tanda kurung siku untuk membebaskan array tumpukan. Jangan lakukan hal seperti ini:

char* one = new char;

delete [] one; // Wrong

char* many = new char [30];

delete many; // Wrong!

6

Jika kebocoran belum diizinkan - Saya biasanya mencari dengan deleaker (periksa di sini: http://deleaker.com ).


3
maaf untuk pertanyaan-komentar tetapi bagaimana dengan parameter fungsi tanpa pointer? someFunction("some parameter")saya harus menghapus "some parameter"dalam someFunction, setelah panggilan fungsi, atau ini secara otomatis dihapus?
19greg96

1
terima kasih atas tautannya ke Deleaker, ini adalah alat yang sangat praktis dengan integrasi yang rapi ke dalam studio visual. Saya bisa menghemat banyak waktu menggunakannya. mengarahkan saya ke garis di mana saya mengalokasikan memori dan tidak membebaskannya. Bagus. Dan itu murah, dibandingkan dengan pencari kebocoran memori lainnya yang saya temukan.
Sendiri

@ john smith plz jelaskan apa cara yang tepat untuk menangani kasus yang mirip dengan kasus 3; str2 = str1; // Buruk! Sekarang 40 byte tidak mungkin gratis. bagaimana cara menghapus str 1 lalu ??
Nihar

1
Bagaimana jika kita menggunakan tipe nilai seperti char *, int, float, ... dan struct seperti Vector, CString, dan tidak menggunakan operator 'baru' sama sekali, itu tidak akan menyebabkan kebocoran memori, benarkan?
123 dimulai

Saya di sini hanya untuk mengatakan saya belum menyentuh c ++ dalam hampir 14 tahun ... tapi saya bangga mengatakan saya mengerti dan ingat bagaimana melakukan semua ini berkat buku c ++ yang masih saya miliki dan baca ketika saya ' saya bosan dengan c #. Buku itu adalah C ++ Efektif dari Scott Mitchell. Ya Tuhan, aku suka buku itu. Scott terima kasih!
JonH

33

Anda dapat menggunakan beberapa teknik dalam kode Anda untuk mendeteksi kebocoran memori. Cara paling umum dan paling mudah untuk dideteksi adalah, tentukan makro katakan, DEBUG_NEW dan gunakan, bersama dengan makro yang sudah ditentukan seperti __FILE__dan __LINE__untuk menemukan kebocoran memori dalam kode Anda. Makro yang telah ditentukan ini memberi tahu Anda file dan nomor baris kebocoran memori.

DEBUG_NEW hanyalah MACRO yang biasanya didefinisikan sebagai:

#define DEBUG_NEW new(__FILE__, __LINE__)
#define new DEBUG_NEW

Sehingga di mana pun Anda menggunakan new, itu juga dapat melacak file dan nomor baris yang dapat digunakan untuk menemukan kebocoran memori dalam program Anda.

Dan __FILE__, __LINE__adalah makro yang telah ditetapkan yang mengevaluasi masing-masing nama file dan nomor baris tempat Anda menggunakannya!

Baca artikel berikut yang menjelaskan teknik menggunakan DEBUG_NEW dengan makro menarik lainnya, sangat indah:

Detektor Kebocoran Memori Cross-Platform


Dari Wikpedia ,

Debug_new mengacu pada teknik dalam C ++ untuk membebani dan / atau mendefinisikan ulang operator yang baru dan operator hapus untuk mencegat alokasi memori dan panggilan deallokasi, dan dengan demikian men-debug program untuk penggunaan memori. Ini sering melibatkan mendefinisikan makro bernama DEBUG_NEW, dan membuat baru menjadi sesuatu seperti baru (_ FILE _, _ LINE _) untuk merekam file / informasi baris tentang alokasi.Microsoft Visual C ++ menggunakan teknik ini di Microsoft Foundation Classes. Ada beberapa cara untuk memperluas metode ini untuk menghindari penggunaan redefinisi makro sementara masih dapat menampilkan informasi file / baris pada beberapa platform. Ada banyak batasan yang melekat pada metode ini. Ini hanya berlaku untuk C ++, dan tidak dapat menangkap kebocoran memori oleh fungsi C seperti malloc. Namun, ini bisa sangat sederhana untuk digunakan dan juga sangat cepat, jika dibandingkan dengan beberapa solusi debugger memori yang lebih lengkap.


4
ini #defineakan mengacaukan kelebihan beban operator newdan menghasilkan kesalahan kompilator. Bahkan jika Anda berhasil mengatasinya maka fungsi yang kelebihan beban tidak akan ditangani. Meskipun tekniknya bagus, terkadang dibutuhkan banyak perubahan kode.
iammilind

1
@ iammilind: Tentu saja, teknik ini bukan solusi untuk semua masalah dan pasti tidak dapat diterapkan di semua situasi.
Nawaz

@Chris_vr: auto_ptrtidak akan berfungsi dengan kontainer standar seperti std::vector, std::listdll. Lihat ini: stackoverflow.com/questions/111478/…
Nawaz

Oke keren FILE dan garis dijelaskan. Apa operator newdan apa versi ini yang Anda gunakan?

14

Ada beberapa teknik pemrograman terkenal yang akan membantu Anda meminimalkan risiko kebocoran memori secara langsung:

  • jika Anda harus melakukan alokasi memori dinamis Anda sendiri, tulis newdan deleteselalu berpasangan, dan pastikan kode alokasi / deallokasi disebut berpasangan
  • hindari alokasi memori dinamis jika Anda bisa. Sebagai contoh, gunakan vector<T> tdimanapun memungkinkan alih-alihT* t = new T[size]
  • gunakan "smart pointer" seperti boost pointer pintar ( http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/smart_ptr.htm )
  • favorit pribadi saya: pastikan Anda telah memahami konsep kepemilikan pointer, dan pastikan bahwa di mana pun Anda menggunakan pointer, Anda tahu entitas kode mana yang merupakan pemiliknya.
  • pelajari operator konstruktor / penugasan mana yang secara otomatis dibuat oleh kompiler C ++, dan apa artinya jika Anda memiliki kelas yang memiliki pointer (atau apa artinya jika Anda memiliki kelas yang berisi pointer ke objek yang bukan miliknya).

Saya menggunakan auto_pointer dari suatu objek artinya itu akan menghapus semua pointer objek kelas lainnya di dalamnya.
Chris_vr

@ Chris_vr: jika Anda memiliki pertanyaan spesifik tentang auto_pointer, saya sarankan Anda membuat pertanyaan baru, termasuk sebuah contoh.
Doc Brown

Banyak posting yang memberi tahu saya bahwa vektor <> tidak menjamin memori dibebaskan setelah dihapus. Saya secara pribadi menguji swap barang dll dan saya sampai pada kesimpulan bahwa vektor <> bocor terutama ketika digunakan secara dinamis. Saya tidak mengerti bagaimana vektor <> dapat disarankan tentang alokasi dinamis do-it-yourself menggunakan 'baru' dan pembersihan dengan benar. Dalam program tertanam saya, saya menghindari menggunakan vektor <> untuk hal-hal dinamis karena semua kebocoran. Di sana saya menggunakan baru atau std :: list
bart s

Saya mengetik perintah kedua karena jumlah karakter. Sayangnya dalam c ++ saya yang tertanam Saya memiliki c ++ lama (98?) Yang tidak memiliki shrink_to_fit pada vektor ... Namun program yang disematkan 100% pasti benar-benar macet ketika kehabisan memori menggunakan vektor <> secara dinamis
bart s


8
  1. Unduh Alat Debugging untuk Windows .
  2. Gunakan gflagsutilitas untuk menghidupkan jejak tumpukan mode pengguna.
  3. Gunakan UMDHuntuk mengambil beberapa snapshot dari memori program Anda. Ambil snapshot sebelum memori dialokasikan, dan ambil snapshot kedua setelah titik di mana Anda yakin bahwa program Anda memiliki memori yang bocor. Anda mungkin ingin menambahkan jeda atau konfirmasi dalam program Anda untuk memberi Anda kesempatan untuk menjalankan UMDHdan mengambil snapshot.
  4. Jalankan UMDHlagi, kali ini dalam mode yang melakukan perbedaan antara dua foto. Ini kemudian akan menghasilkan laporan yang berisi tumpukan panggilan kebocoran memori yang diduga.
  5. Kembalikan gflagspengaturan sebelumnya ketika Anda selesai.

UMDHakan memberi Anda lebih banyak informasi daripada tumpukan debug CRT karena memantau alokasi memori di seluruh proses Anda; bahkan dapat memberi tahu Anda jika komponen pihak ketiga bocor.


1
Saya lebih suka Deleaker dan Valgrind daripada profiler standar
z0r1fan

8

Menjalankan "Valgrind" dapat:

1) Membantu Mengidentifikasi Kebocoran Memori - menunjukkan kepada Anda berapa banyak memori yang bocor yang Anda miliki, dan tunjukkan ke baris-baris dalam kode tempat memori yang bocor dialokasikan.

2) Tunjukkan upaya yang salah untuk membebaskan memori (mis. Panggilan yang salah delete)

Petunjuk untuk menggunakan "Valgrind"

1) Dapatkan valgrind di sini .

2) Kompilasi kode Anda dengan -gbendera

3) Dalam menjalankan shell Anda:

valgrind --leak-check=yes myprog arg1 arg2

Di mana "myprog" adalah program yang dikompilasi dan arg1, arg2argumen program Anda.

4) Hasilnya adalah daftar panggilan ke malloc/ newyang tidak memiliki panggilan berikutnya untuk dihapus secara gratis.

Sebagai contoh:

==4230==    at 0x1B977DD0: malloc (vg_replace_malloc.c:136)

==4230==    by 0x804990F: main (example.c:6)

Memberitahu Anda di baris mana malloc(yang tidak dibebaskan) dipanggil.

Sebagaimana ditunjukkan oleh orang lain, pastikan bahwa untuk setiap new/ mallocpanggilan, Anda memiliki delete/ freepanggilan berikutnya .


6

Jika Anda menggunakan gcc, tersedia gprof.

Saya ingin tahu bagaimana programmer menemukan kebocoran memori

Beberapa menggunakan alat, beberapa melakukan apa yang Anda lakukan, bisa juga melalui tinjauan kode rekan

Apakah ada standar atau prosedur yang harus diikuti untuk memastikan tidak ada kebocoran memori dalam program

Bagi saya: setiap kali saya membuat objek yang dialokasikan secara dinamis, saya selalu meletakkan kode pembebasan setelah itu, lalu mengisi kode di antaranya. Ini akan baik-baik saja jika Anda yakin tidak akan ada pengecualian dalam kode di antaranya. Kalau tidak, saya menggunakan try-akhirnya (saya tidak sering menggunakan C ++).


beberapa waktu kami tidak dapat menghapus dialokasikan dalam konstruktor. Apa yang harus dilakukan pada kesempatan itu.
Chris_vr

5
  1. Di studio visual, ada detektor built-in untuk kebocoran memori yang disebut C Runtime Library. Ketika program Anda keluar setelah fungsi utama kembali, CRT akan memeriksa tumpukan debug aplikasi Anda. jika Anda memiliki blok yang masih dialokasikan pada tumpukan debug, maka Anda memiliki kebocoran memori ..

  2. Forum ini membahas beberapa cara untuk menghindari kebocoran memori di C / C ++ ..


5

Cari kode Anda untuk kemunculan new, dan pastikan semuanya terjadi dalam konstruktor dengan penghapusan yang cocok di destruktor. Pastikan bahwa ini adalah satu-satunya operasi melempar yang mungkin di konstruktor itu. Cara sederhana untuk melakukan ini adalah dengan membungkus semua pointer std::auto_ptr, atau boost::scoped_ptr(tergantung pada apakah Anda perlu memindahkan semantik). Untuk semua kode di masa depan, pastikan saja bahwa setiap sumber daya dimiliki oleh objek yang membersihkan sumber daya di destruktornya. Jika Anda perlu memindahkan semantik maka Anda dapat memutakhirkan ke kompiler yang mendukung r-value reference (VS2010 menurut saya) dan membuat move constructor. Jika Anda tidak ingin melakukan itu maka Anda dapat menggunakan berbagai teknik rumit yang melibatkan penggunaan swap yang teliti, atau coba perpustakaan Boost.Move.


itu tidak selalu mungkin untuk menghapus memori yang dialokasikan dalam konstruktor.
Bagaimana

@Chris_vr Apa maksudmu? Jika semua anggota pointer Anda adalah scope_ptrs, dan masing-masing diinisialisasi secara individual maka semua yang berhasil dibangun akan menghapus pointer mereka, dan yang lain belum akan memegang pointer ke memori yang dialokasikan. Saya akan memberikan contoh dalam beberapa jam ketika saya pulang dari kerja.
Mankarse

@Chris_vr: jika Anda memiliki contoh spesifik, kirimkan sebagai pertanyaan baru, sehingga kami dapat membahasnya di sana.
Doc Brown

5

Anda dapat menggunakan alat Valgrind untuk mendeteksi kebocoran memori.

Juga, untuk menemukan kebocoran pada fungsi tertentu, gunakan keluar (0) di akhir fungsi dan kemudian jalankan dengan Valgrind

`$` valgrind ./your_CPP_program 

5

Survei pemeriksa kebocoran memori otomatis

Dalam jawaban ini, saya membandingkan beberapa kebocoran kebocoran memori yang berbeda dalam contoh kebocoran memori yang mudah dimengerti.

Sebelum apa pun, lihat tabel besar ini di wiki ASan yang membandingkan semua alat yang diketahui oleh manusia: https://github.com/google/sanitizers/wiki/AddressSanitizerComparisonOfMemoryTools/d06210f759fec97066888e5f27c7e722832b0924

Contoh yang dianalisis adalah:

main.c

#include <stdlib.h>

void * my_malloc(size_t n) {
    return malloc(n);
}

void leaky(size_t n, int do_leak) {
    void *p = my_malloc(n);
    if (!do_leak) {
        free(p);
    }
}

int main(void) {
    leaky(0x10, 0);
    leaky(0x10, 1);
    leaky(0x100, 0);
    leaky(0x100, 1);
    leaky(0x1000, 0);
    leaky(0x1000, 1);
}

GitHub hulu .

Kami akan mencoba melihat seberapa jelas alat yang berbeda mengarahkan kami ke panggilan yang bocor.

tcmalloc dari gperftools oleh Google

https://github.com/gperftools/gperftools

Penggunaan di Ubuntu 19.04:

sudo apt-get install google-perftools
gcc -ggdb3 -o main.out main.c -ltcmalloc
PPROF_PATH=/usr/bin/google-pprof \
  HEAPCHECK=normal \
  HEAPPROFILE=ble \
  ./main.out \
;
google-pprof main.out ble.0001.heap --text

Output dari program yang dijalankan berisi analisis kebocoran memori:

WARNING: Perftools heap leak checker is active -- Performance may suffer
Starting tracking the heap
Dumping heap profile to ble.0001.heap (Exiting, 4 kB in use)
Have memory regions w/o callers: might report false leaks
Leak check _main_ detected leaks of 272 bytes in 2 objects
The 2 largest leaks:
Using local file ./main.out.
Leak of 256 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581d3 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start
Leak of 16 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581b5 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start


If the preceding stack traces are not enough to find the leaks, try running THIS shell command:

pprof ./main.out "/tmp/main.out.24744._main_-end.heap" --inuse_objects --lines --heapcheck  --edgefraction=1e-10 --nodefraction=1e-10 --gv

If you are still puzzled about why the leaks are there, try rerunning this program with HEAP_CHECK_TEST_POINTER_ALIGNMENT=1 and/or with HEAP_CHECK_MAX_POINTER_OFFSET=-1
If the leak report occurs in a small fraction of runs, try running with TCMALLOC_MAX_FREE_QUEUE_SIZE of few hundred MB or with TCMALLOC_RECLAIM_MEMORY=false, it might help find leaks more re
Exiting with error code (instead of crashing) because of whole-program memory leaks

dan output google-pprofberisi analisis penggunaan tumpukan:

Using local file main.out.
Using local file ble.0001.heap.
Total: 0.0 MB
     0.0 100.0% 100.0%      0.0 100.0% my_malloc
     0.0   0.0% 100.0%      0.0 100.0% __libc_start_main
     0.0   0.0% 100.0%      0.0 100.0% _start
     0.0   0.0% 100.0%      0.0 100.0% leaky
     0.0   0.0% 100.0%      0.0 100.0% main

Output menunjukkan kita ke dua dari tiga kebocoran:

Leak of 256 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581d3 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start
Leak of 16 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581b5 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start

Saya tidak yakin mengapa yang ketiga tidak muncul

Bagaimanapun, ketika biasanya ketika sesuatu bocor, itu terjadi berkali-kali, dan ketika saya menggunakannya pada proyek nyata, saya akhirnya ditunjukkan ke fungsi bocor dengan sangat mudah.

Seperti yang disebutkan pada output itu sendiri, ini menyebabkan pelambatan eksekusi yang signifikan.

Dokumentasi lebih lanjut di:

Lihat juga: Cara Menggunakan TCMalloc?

Diuji di Ubuntu 19.04, google-perftools 2.5-2.

Address Sanitizer (ASan) juga oleh Google

https://github.com/google/sanitizers

Disebutkan sebelumnya di: Bagaimana menemukan kebocoran memori dalam suatu kode / proyek C ++? TODO vs tcmalloc.

Ini sudah terintegrasi ke dalam GCC, jadi Anda bisa melakukan:

gcc -fsanitize=address -ggdb3 -o main.out main.c
./main.out 

dan output eksekusi:

=================================================================
==27223==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 4096 byte(s) in 1 object(s) allocated from:
    #0 0x7fabbefc5448 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c448)
    #1 0x55bf86c5f17c in my_malloc /home/ciro/test/main.c:4
    #2 0x55bf86c5f199 in leaky /home/ciro/test/main.c:8
    #3 0x55bf86c5f210 in main /home/ciro/test/main.c:20
    #4 0x7fabbecf4b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)

Direct leak of 256 byte(s) in 1 object(s) allocated from:
    #0 0x7fabbefc5448 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c448)
    #1 0x55bf86c5f17c in my_malloc /home/ciro/test/main.c:4
    #2 0x55bf86c5f199 in leaky /home/ciro/test/main.c:8
    #3 0x55bf86c5f1f2 in main /home/ciro/test/main.c:18
    #4 0x7fabbecf4b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)

Direct leak of 16 byte(s) in 1 object(s) allocated from:
    #0 0x7fabbefc5448 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c448)
    #1 0x55bf86c5f17c in my_malloc /home/ciro/test/main.c:4
    #2 0x55bf86c5f199 in leaky /home/ciro/test/main.c:8
    #3 0x55bf86c5f1d4 in main /home/ciro/test/main.c:16
    #4 0x7fabbecf4b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)

SUMMARY: AddressSanitizer: 4368 byte(s) leaked in 3 allocation(s).

yang secara jelas mengidentifikasi semua kebocoran. Bagus!

ASan juga dapat melakukan pemeriksaan keren lainnya seperti menulis di luar batas: Penghancuran tumpukan terdeteksi

Diuji di Ubuntu 19.04, GCC 8.3.0.

Valgrind

http://www.valgrind.org/

Sebelumnya disebutkan di: https://stackoverflow.com/a/37661630/895245

Pemakaian:

sudo apt-get install valgrind
gcc -ggdb3 -o main.out main.c
valgrind --leak-check=yes ./main.out

Keluaran:

==32178== Memcheck, a memory error detector
==32178== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==32178== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==32178== Command: ./main.out
==32178== 
==32178== 
==32178== HEAP SUMMARY:
==32178==     in use at exit: 4,368 bytes in 3 blocks
==32178==   total heap usage: 6 allocs, 3 frees, 8,736 bytes allocated
==32178== 
==32178== 16 bytes in 1 blocks are definitely lost in loss record 1 of 3
==32178==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==32178==    by 0x10915C: my_malloc (main.c:4)
==32178==    by 0x109179: leaky (main.c:8)
==32178==    by 0x1091B4: main (main.c:16)
==32178== 
==32178== 256 bytes in 1 blocks are definitely lost in loss record 2 of 3
==32178==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==32178==    by 0x10915C: my_malloc (main.c:4)
==32178==    by 0x109179: leaky (main.c:8)
==32178==    by 0x1091D2: main (main.c:18)
==32178== 
==32178== 4,096 bytes in 1 blocks are definitely lost in loss record 3 of 3
==32178==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==32178==    by 0x10915C: my_malloc (main.c:4)
==32178==    by 0x109179: leaky (main.c:8)
==32178==    by 0x1091F0: main (main.c:20)
==32178== 
==32178== LEAK SUMMARY:
==32178==    definitely lost: 4,368 bytes in 3 blocks
==32178==    indirectly lost: 0 bytes in 0 blocks
==32178==      possibly lost: 0 bytes in 0 blocks
==32178==    still reachable: 0 bytes in 0 blocks
==32178==         suppressed: 0 bytes in 0 blocks
==32178== 
==32178== For counts of detected and suppressed errors, rerun with: -v
==32178== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

Jadi sekali lagi, semua kebocoran terdeteksi.

Lihat juga: Bagaimana cara menggunakan valgrind untuk menemukan kebocoran memori?

Diuji di Ubuntu 19.04, valgrind 3.14.0.


4

Visual Leak Detector (VLD) adalah sistem deteksi kebocoran memori open-source gratis, kuat untuk Visual C ++.

Ketika Anda menjalankan program Anda di bawah Visual Studio debugger, Visual Leak Detector akan menampilkan laporan kebocoran memori di akhir sesi debugging Anda. Laporan kebocoran mencakup tumpukan panggilan lengkap yang menunjukkan bagaimana setiap blok memori yang bocor dialokasikan. Klik dua kali pada baris di tumpukan panggilan untuk melompat ke file itu dan baris di jendela editor.

Jika Anda hanya memiliki crash dumps, Anda dapat menggunakan !heap -lperintah Windbg , itu akan mendeteksi blok heap yang bocor. Lebih baik buka opsi gflags: "Buat basis data jejak jejak mode pengguna", maka Anda akan melihat tumpukan panggilan alokasi memori.


4

MTuner adalah alat profil memori multi platform gratis, deteksi kebocoran, dan alat analisis yang mendukung kompiler MSVC, GCC, dan Clang. Fitur termasuk:

  • riwayat penggunaan memori berdasarkan blok waktu dan blok memori langsung
  • pemfilteran operasi memori yang kuat berdasarkan tumpukan, tag memori, rentang waktu, dll.
  • SDK untuk instrumentasi manual dengan kode sumber lengkap
  • dukungan integrasi berkelanjutan melalui penggunaan baris perintah
  • panggilan susunan pohon dan navigasi peta pohon
  • lebih banyak.

Pengguna dapat membuat profil platform penargetan perangkat lunak apa pun dengan kompiler GCC atau Clang. MTuner hadir dengan dukungan bawaan untuk platform Windows, PlayStation 4 dan PlayStation 3.


Ini harus menjadi jawaban yang diterima. Ini adalah alat yang hebat dan dapat menangani volume alokasi / alokasi yang tidak dapat dilakukan orang lain.
Serge Rogatch

3

Pada Windows Anda dapat menggunakan tumpukan debug CRT .

Apakah ada standar atau prosedur yang harus diikuti untuk memastikan tidak ada kebocoran memori dalam program.

Ya, jangan gunakan manajemen memori manual (jika Anda pernah menelepon deleteatau delete[]secara manual, maka Anda salah melakukannya). Gunakan RAII dan smart pointer, batasi alokasi heap hingga minimum absolut (sebagian besar waktu, variabel otomatis sudah cukup).


3

Menjawab bagian kedua dari pertanyaan Anda,

Apakah ada standar atau prosedur yang harus diikuti untuk memastikan tidak ada kebocoran memori dalam program.

Ya ada. Dan ini adalah salah satu perbedaan utama antara C dan C ++.

Dalam C ++, Anda tidak boleh menelepon newatau deletedalam kode pengguna Anda. RAII adalah teknik yang sangat umum digunakan, yang cukup banyak memecahkan masalah manajemen sumber daya. Setiap sumber daya dalam program Anda (sumber daya adalah apa saja yang harus diperoleh, dan kemudian, dirilis: pegangan file, soket jaringan, koneksi basis data, tetapi juga alokasi memori biasa, dan dalam beberapa kasus, pasangan panggilan API (BeginX ( ) / EndX (), LockY (), UnlockY ()), harus dibungkus dalam kelas, di mana:

  • konstruktor memperoleh sumber daya (dengan memanggil newjika sumber daya itu adalah alokasi memroy)
  • destructor melepaskan sumber daya,
  • penyalinan dan penugasan dapat dicegah (dengan membuat pembangun salinan dan operator penugasan pribadi), atau diterapkan untuk bekerja dengan benar (misalnya dengan mengkloning sumber daya yang mendasarinya)

Kelas ini kemudian dipakai secara lokal, di stack, atau sebagai anggota kelas, dan bukan dengan memanggil newdan menyimpan pointer.

Anda sering tidak perlu mendefinisikan sendiri kelas-kelas ini. Wadah pustaka standar berperilaku dengan cara ini juga, sehingga setiap objek yang disimpan ke dalam std::vectorakan dibebaskan ketika vektor dihancurkan. Jadi sekali lagi, jangan menyimpan pointer ke dalam wadah (yang mengharuskan Anda menelepon newdan delete), tetapi objek itu sendiri (yang memberi Anda manajemen memori gratis ). Demikian juga, kelas penunjuk pintar dapat digunakan untuk dengan mudah membungkus objek yang hanya harus dialokasikan dengan new, dan mengontrol masa hidup mereka.

Ini berarti bahwa ketika objek keluar dari ruang lingkup, ia secara otomatis dihancurkan, dan sumber dayanya dilepaskan dan dibersihkan.

Jika Anda melakukan ini secara konsisten di seluruh kode Anda, Anda tidak akan memiliki kebocoran memori. Segala sesuatu yang bisa bocor terkait dengan destruktor yang dijamin akan dipanggil ketika kontrol meninggalkan ruang lingkup di mana objek tersebut dinyatakan.


jika smart pointer memiliki kelas dan kelas tersebut berisi pointer dari beberapa kelas lainnya. ketika smart berbunyi apakah itu berarti semua pointer di dalamnya akan dihapus dengan aman.
Chris_vr

@ Chris: Dengan asumsi bahwa objek yang ditunjuk oleh penunjuk pintar memiliki destruktor yang melakukan pembersihan yang diperlukan atau objek berisi anggota yang memiliki destruktor sendiri untuk melakukan pembersihan yang diperlukan. Intinya, selama setiap objek mengurus dirinya sendiri (membersihkan setelah dirinya sendiri ketika dihancurkan), dan selama setiap objek disimpan oleh nilai, bukan sebagai pointer, maka segala sesuatu yang perlu dibebaskan akan dibebaskan.
jalf

3

AddressSanitizer (ASan) adalah pendeteksi kesalahan memori yang cepat. Ia menemukan bug yang digunakan-setelah-bebas dan {heap, stack, global} -buffer dalam program C / C ++. Ia menemukan:

  • Gunakan setelah bebas (menggantung dereference pointer)
  • Tumpukan buffer overflow
  • Stack buffer overflow
  • Overflow buffer global
  • Gunakan setelah kembali
  • Inisialisasi bug pesanan

Alat ini sangat cepat. Perlambatan rata-rata dari program instrument adalah ~ 2x.



0

Selain alat dan metode yang disediakan dalam jawaban lain, alat analisis kode statis dapat digunakan untuk mendeteksi kebocoran memori (dan masalah lainnya juga). Alat gratis yang tangguh adalah Cppcheck. Tetapi ada banyak alat lain yang tersedia. Wikipedia memiliki daftar alat analisis kode statis.


-1

Pastikan bahwa semua memori tumpukan berhasil dibebaskan. Tidak perlu jika Anda tidak pernah mengalokasikan memori pada heap. Jika ya, hitung berapa kali Anda menyimpan memori, dan hitung berapa kali Anda membebaskan memori.


-3

"Baru" atau "hapus" tidak boleh digunakan dalam kode aplikasi. Sebaliknya, buat tipe baru yang menggunakan idiom manajer / pekerja, di mana kelas manajer mengalokasikan dan membebaskan memori dan meneruskan semua operasi lainnya ke objek pekerja.

Sayangnya ini lebih banyak pekerjaan daripada seharusnya karena C ++ tidak memiliki "operator" yang berlebihan. Ini bahkan lebih banyak bekerja di hadapan polimorfisme.

Tapi ini sepadan dengan usaha karena Anda tidak perlu khawatir tentang kebocoran memori, yang berarti Anda bahkan tidak perlu mencarinya.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.