C ++ kodunda / projesinde bellek sızıntısı nasıl bulunur?


180

Windows platformunda bir C ++ programcısıyım. Visual Studio 2008 kullanıyorum.

Ben genellikle kod bellek sızıntıları ile sonuçlanır.

Normalde kodu inceleyerek bellek sızıntısını buluyorum, ama hantal ve her zaman iyi bir yaklaşım değil.

Ücretli bir bellek sızıntısı algılama aracı ödeyemediğim için, sizden bellek sızıntılarını önlemek için mümkün olan en iyi yolları önermenizi istedim.

  1. Programlayıcının bellek sızıntılarını nasıl bulabileceğini bilmek istiyorum.
  2. Programda bellek sızıntısı olmadığından emin olmak için izlenmesi gereken herhangi bir standart veya prosedür var mı?

29
"Genellikle kodda bellek sızıntısı oluyor." Otomatik değişkenler, kapsayıcılar ve akıllı işaretçiler kullanırsanız (ve akıllı işaretçiler kullanmak için en iyi uygulamaları izlerseniz), bellek sızıntıları çok nadir olmalıdır. Hemen hemen her durumda otomatik kaynak yönetimini kullanmanız gerektiğini unutmayın .
James McNellis


1
@Hostile Fork: "kişi genellikle bellek sızıntılarına neden olmaktan nasıl kaçınılabilir? " Bu yanıtların kapsamında değildir.
Doc Brown

2
@Doc Brown: Buna bakmak istemiyordum, ancak her şey başka bir yerde, örneğin stackoverflow.com/questions/45627/…
değil

1
DIY Kaçak dedektörü: Şüpheli bir kodu sonsuz bir döngüye koyabilir ve daha sonra bir görev yöneticisi açabilirsiniz, genellikle küçük bir sızıntı bile hafızayı veya dakikaları doldurur (Bu, kod karmaşıklığınıza ve CPU'nuza bağlıdır). Bu gerçekleşmezse, bu kod parçası sızıntı yapmaz.
Merhaba Dünya

Yanıtlar:


270

Talimatlar

İhtiyacınız Olan Şeyler

  • C ++ 'da Yeterlik
  • C ++ derleyicisi
  • Hata ayıklayıcı ve diğer araştırmacı yazılım araçları

1

Operatör temellerini öğrenin. C ++ operatörü newyığın belleği ayırır. deleteOperatör boşaltır yığın bellek. Her biri için new, deleteayırdığınız aynı belleği boşaltmak için bir kullanmalısınız :

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

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

2

Belleği yalnızca sildiyseniz yeniden tahsis edin. Aşağıdaki kodda str, ikinci ayırma ile yeni bir adres alınır. İlk adres geri döndürülemez bir şekilde kaybolur ve işaret ettiği 30 bayt da. Şimdi özgür olmak imkansız ve hafıza sızıntısı var:

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

İşaretçi atamalarını izleyin. Her dinamik değişkenin (öbek üzerinde ayrılan bellek) bir işaretçi ile ilişkilendirilmesi gerekir. Dinamik bir değişken işaretçilerden ayrıldığında, silmek imkansız hale gelir. Yine, bu bir bellek sızıntısı ile sonuçlanır:

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

Yerel göstergelere dikkat edin. Bir işlevde bildirdiğiniz bir işaretçi yığına ayrılır, ancak işaret ettiği dinamik değişken yığın üzerinde ayrılır. Bunu silmezseniz, program işlevden çıktıktan sonra da devam eder:

void Leak(int x){

char* p = new char [x];

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

}

5

"Sil" işleminden sonra köşeli parantezlere dikkat edin. deleteTek bir nesneyi serbest bırakmak için tek başına kullanın . delete []Bir yığın dizisini serbest bırakmak için köşeli parantezlerle kullanın . Böyle bir şey yapma:

char* one = new char;

delete [] one; // Wrong

char* many = new char [30];

delete many; // Wrong!

6

Sızıntı henüz izin verildiyse - genellikle daha iyi araştırıyorum (buradan kontrol edin: http://deleaker.com ).


3
soru-yorum için özür dilerim ama işaretçi olmayan fonksiyon parametreleri ne olacak? someFunction("some parameter")Ben silmek zorunda "some parameter"içinde someFunctionişlev çağrısından sonra, ya da bu otomatik olarak silinir?
19greg96

1
Deleaker'a bağlandığınız için teşekkür ederiz, görsel stüdyoya düzgün bir entegrasyona sahip gerçekten kullanışlı bir araçtır. Bunu kullanarak çok zaman kazanabilirim. hafızayı ayırdığım ve serbest bırakmadığım çizgilere işaret etti. Harika. Ve bulduğum diğer bellek sızıntısı bulucularına kıyasla ucuz.
this.myself

@ john smith plz açıklama 3 vaka benzer davaları işlemek için doğru yolu nedir; str2 = str1; // Kötü! Şimdi 40 baytın boşaltılması imkansız. str 1 sonra nasıl silinir ??
Nihar

1
Char *, int, float, ... gibi bir değer türü ve Vector, CString gibi bir yapı kullanırsak ve 'yeni' operatör kullanmazsak, bellek sızıntısına neden olmaz, doğru mu?
123iamking

Ben sadece 14 yıldır c ++ 'a dokunmadığımı söylemek için buradayım ... ama hala sahip olduğum ve okuduğumda okuduğum bir c ++ kitabı sayesinde tüm bunları anladığımı ve nasıl yapacağımı hatırladığım için gurur duyuyorum.' m c # ile sıkılmış. Bu kitap Scott Mitchell'in Etkili C ++. Tanrım bu kitabı çok sevdim. Teşekkürler Scott!
JonH

33

Bellek sızıntısını tespit etmek için kodunuzdaki bazı teknikleri kullanabilirsiniz. Algılamak için en yaygın ve en kolay yolu bir makro söz, DEBUG_NEW tanımlamak ve benzeri önceden makrolarıyla birlikte, kullanmak olduğunu __FILE__ve __LINE__kodunuzda bellek sızıntısı bulmak için. Bu önceden tanımlanmış makrolar size bellek sızıntılarının dosya ve satır numarasını bildirir.

DEBUG_NEW sadece bir MACRO'dur ve genellikle şu şekilde tanımlanır:

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

Böylece, nerede kullanırsanız kullanın new, programınızdaki bellek sızıntısını bulmak için kullanılabilecek dosya ve satır numarasını da takip edebilir.

Ve __FILE__, __LINE__olan makro önceden bunları kullanmak nerede sırasıyla dosya adı ve satır numarası değerlendirmek!

DEBUG_NEW'i diğer ilginç makrolarla kullanma tekniğini çok güzel açıklayan aşağıdaki makaleyi okuyun:

Platformlar Arası Bellek Kaçak Dedektörü


Gönderen Wikpedia ,

Debug_new, bellek tahsisi ve yeniden konumlandırma çağrılarını durdurmak ve böylece bellek kullanımı için bir programda hata ayıklamak için operatörün yeni ve operatör silmesini aşırı yüklemek ve / veya yeniden tanımlamak için C ++ 'da kullanılan bir tekniği ifade eder. Genellikle DEBUG_NEW adlı bir makro tanımlamayı içerir ve ayırma üzerine dosya / satır bilgilerini kaydetmek için yeniyi yeni (_ FILE _, _ LINE _) gibi bir şey haline getirir .Microsoft Visual C ++, Microsoft Foundation Classes bu tekniği kullanır. Bazı platformlarda dosya / satır bilgilerini göstermeye devam ederken makro yeniden tanımlamayı kullanmaktan kaçınmak için bu yöntemi genişletmenin bazı yolları vardır. Bu yöntemin doğasında var olan birçok sınırlama vardır. Yalnızca C ++ için geçerlidir ve malloc gibi C işlevleri tarafından bellek sızıntılarını yakalayamaz. Bununla birlikte, bazı daha eksiksiz bellek hata ayıklayıcı çözümlerine kıyasla kullanımı çok basit ve çok hızlı olabilir.


4
bu #define, aşırı yüklenmiş karışıklık operator newyaratır ve derleyici hataları oluşturur. Bunun üstesinden gelmeyi başarsanız bile, aşırı yüklenmiş fonksiyonlar ele alınmayacaktır. Teknik iyi olsa da, bazen çok fazla kod değişikliğine ihtiyaç duyar.
iammilind

1
@iammilind: Tabii ki, bu teknik tüm sorunların tamamen iyileştirilmiş bir çözümü değildir ve her durumda kesinlikle geçerli değildir.
Nawaz

@Chris_vr: auto_ptrgibi standart konteynerlerin ile olmaz işin std::vector, std::list: vb Bu bak stackoverflow.com/questions/111478/...
Navaz

Peki tamam. DOSYA ve satır açıklanmaktadır. Kullandığınız operator newbu sürümleri nedir ve nelerdir?

14

İlk elden bellek sızıntısı alma riskini en aza indirmenize yardımcı olacak iyi bilinen bazı programlama teknikleri vardır:

  • kendi dinamik bellek ayırma işleminizi yapmanız gerekiyorsa, her zaman çift olarak yazın newve deleteayırın ve ayırma / ayırma kodunun çift olarak adlandırıldığından emin olun
  • mümkünse dinamik bellek ayırmadan kaçının. Örneğin, vector<T> tyerine mümkün olan her yerde kullanınT* t = new T[size]
  • destek akıllı işaretçiler gibi "akıllı işaretçiler" kullanın ( http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/smart_ptr.htm )
  • kişisel favorim: bir işaretçinin sahiplik kavramını anladığınızdan emin olun ve işaretçileri kullandığınız her yerde, hangi kod varlığının sahibi olduğunu bildiğinizden emin olun
  • C ++ derleyicisi tarafından otomatik olarak hangi kurucuların / atama işleçlerinin oluşturulduğunu ve bir işaretçiye sahip olan sınıfınız varsa (veya sahip olmadığı bir nesneye işaretçi içeren bir sınıfınız varsa bunun ne anlama geldiğini) öğrenin .

Ben bir nesnenin auto_pointer kullanın içindeki tüm diğer sınıf nesne işaretçisi sileceği anlamına gelir.
Chris_vr

@Chris_vr: auto_pointer hakkında özel bir sorunuz varsa, bir örnek de dahil olmak üzere yeni bir soru yapmanızı öneririm.
Doc Brown

Birçok yazı bana <> vektörünün hafızanın boşaltıldığını garanti etmediğini söylüyor. Ben şahsen takas şeyler vb test ve özellikle dinamik olarak kullanıldığında vektör <> sızıntı olduğu sonucuna vardım. '<' Vektörü ve doğru temizleme kullanarak vektöre <> kendin yap dinamik ayırma konusunda nasıl önerilebileceğini anlamıyorum. Gömülü programlarda, tüm sızıntılar nedeniyle dinamik şeyler için vektör <> kullanmaktan kaçınırım. Orada yeni veya std kullanıyorum :: list
bart s

Karakter sayısı nedeniyle ikinci bir komut yazıyorum. Ne yazık ki benim gömülü c ++ bir vektör üzerinde shrink_to_fit olmayan eski bir c ++ (98?) Var ... Ancak gömülü program% 100 vektör <> dinamik olarak kullanarak bellek bittiğinde tamamen çökmesine emin
bart s


8
  1. Windows için Hata Ayıklama Araçları'nı indirin .
  2. gflagsKullanıcı modu yığın izlerini açmak için yardımcı programı kullanın .
  3. UMDHProgramınızın belleğinin birden fazla anlık görüntüsünü almak için kullanın . Bellek ayrılmadan önce bir enstantane çekin ve programınızın belleği sızdırdığını düşündüğünüz bir noktadan sonra ikinci bir enstantane çekin. UMDHAnlık görüntüleri çalıştırma ve alma şansı vermek için programınıza duraklamalar veya istemler eklemek isteyebilirsiniz .
  4. UMDHBu sefer iki anlık görüntü arasında fark yaratan modunda tekrar çalıştırın . Ardından, şüpheli bellek sızıntılarının çağrı yığınlarını içeren bir rapor oluşturur.
  5. İşiniz gflagsbittiğinde önceki ayarlarınızı geri yükleyin .

UMDHtüm işleminiz boyunca bellek ayırmalarını izlediği için CRT hata ayıklama yığınından daha fazla bilgi verecektir; üçüncü taraf bileşenlerin sızdırıp sızdırmadığını bile söyleyebilir.


1
Standart profiler yerine Deleaker ve Valgrind'i tercih ediyorum
z0r1fan

8

"Valgrind" çalıştırmak şunları yapabilir:

1) Bellek Sızıntılarını Tanımlamaya Yardımcı Olun - kaç bellek sızıntısı olduğunu gösterin ve sızdırılan belleğin tahsis edildiği koddaki satırlara dikkat edin.

2) serbest hafızaya yanlış girişimleri dışında Noktası ör uygunsuz çağrı ( delete)

"Valgrind" kullanma talimatları

1) Valgrind'i buradan alın .

2) Kodunuzu -gbayrakla derleyin

3) kabuk çalıştırmak:

valgrind --leak-check=yes myprog arg1 arg2

Nerede "myprog" derlenmiş bir programdır ve arg1,arg2 programınızın argümanlar.

4) Sonuç, daha sonra ücretsiz silme çağrısı olmayan malloc/ çağrılarının listesidir new.

Örneğin:

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

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

Hangi satıra malloc(serbest bırakılmamış) çağrıldığını söyler .

Başkaları tarafından işaret edildiği gibi, her new/ mallocarama için bir sonraki delete/ freearama yaptığınızdan emin olun .


6

Gcc kullanıyorsanız, gprof kullanılabilir.

Programcının bellek sızıntısını nasıl bulduğunu bilmek istedim

Bazıları araçları kullanır, bazıları ne yaparsanız yapın, akran kodu incelemesi yoluyla da olabilir

Programda bellek sızıntısı olmadığından emin olmak için izlenmesi gereken herhangi bir standart veya prosedür var mı?

Benim için: dinamik olarak ayrılmış nesneler oluşturduğumda, her zaman sonra boşaltma kodunu koydum, sonra arasındaki kodu doldurdum. Arasındaki kodda istisnalar olmayacağından eminseniz bu sorun olmaz. Aksi takdirde, try-nihayet kullanıyorum (sık sık C ++ kullanmıyorum).


bir süre yapıcıya ayrılanları silemeyiz. bu durumda ne yapmalı.
Chris_vr

5
  1. Görsel stüdyoda, C Runtime Library adlı bellek sızıntısı için yerleşik bir dedektör vardır. Ana işlev döndükten sonra programınızdan çıkıldığında, CRT uygulamanızın hata ayıklama yığınını kontrol eder. hala hata ayıklama öbek üzerinde tahsis edilmiş herhangi bir blok varsa, o zaman bellek sızıntısı var ..

  2. Bu forum , C / C ++ 'da bellek sızıntısını önlemenin birkaç yolunu ele almaktadır.


5

Kodunuzu oluşumları için arayın newve hepsinin bir yıkıcıda eşleşen silme özelliğine sahip bir kurucu içinde gerçekleştiğinden emin olun. Bunun, yapıcıdaki olası tek fırlatma işlemi olduğundan emin olun. Bunu yapmanın basit bir yolu tüm işaretçileri sarılmasıdır std::auto_ptrveya boost::scoped_ptr(eğer anlambilimini taşımak gerek olup olmadığına bağlı olarak). Gelecekteki tüm kodlar için, her kaynağın kaynağını yıkıcısında temizleyen bir nesneye ait olduğundan emin olun. Taşıma semantiğine ihtiyacınız varsa, r-değeri referanslarını destekleyen bir derleyiciye yükseltebilirsiniz (VS2010 inanıyorum) ve hareket yapıcıları oluşturabilirsiniz. Bunu yapmak istemiyorsanız, takasın bilinçli kullanımını içeren çeşitli zor teknikler kullanabilir veya Boost.Move kütüphanesini deneyebilirsiniz.


yapıcıdaki ayrılan belleği silmek her zaman mümkün değildir.Bu durumla nasıl başa
çıkılır

@Chris_vr Ne demek istiyorsun? Tüm işaretçi üyeleriniz scope_ptrs ve her biri ayrı ayrı başlatılırsa, başarılı bir şekilde oluşturulmuş olanların tümü işaretleyicilerini siler ve diğerleri yine de ayrılmış belleğe işaretçiler tutmaz. İşten eve döndüğümde birkaç saat içinde bir örnek vereceğim.
Mankarse

@Chris_vr: Belirli bir örneğiniz varsa, yeni bir soru olarak gönderin, böylece orada tartışabiliriz.
Doc Brown

5

Bellek sızıntılarını tespit etmek için Valgrind aracını kullanabilirsiniz.

Ayrıca, belirli bir işlevdeki sızıntıyı bulmak için, işlevin sonunda exit (0) kullanın ve sonra Valgrind ile çalıştırın

`$` valgrind ./your_CPP_program 

5

Otomatik bellek sızıntı denetleyicileri

Bu cevapta, anlaması kolay bir bellek sızıntısı örneğinde birkaç farklı bellek sızıntısı denetleyicisini karşılaştırıyorum.

Her şeyden önce, ASan wiki'sinde insan tarafından bilinen tüm araçları karşılaştıran bu büyük tabloya bakın: https://github.com/google/sanitizers/wiki/AddressSanitizerComparisonOfMemoryTools/d06210f759fec97066888e5f27c7e722832b0924

Analiz edilen örnek şöyle olacaktır:

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

Yukarı Git .

Farklı araçların bizi sızdıran çağrılara ne kadar net yönlendirdiğini görmeye çalışacağız.

Google'dan gperftools'tan tcmalloc

https://github.com/gperftools/gperftools

Ubuntu 19.04'te kullanımı:

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

Program çalıştırmasının çıktısı bellek sızıntısı analizini içerir:

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

ve çıktısı google-pprofyığın kullanım analizini içerir:

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

Çıktı bizi üç sızıntıdan ikisine işaret ediyor:

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

Üçüncüsünün neden görünmediğinden emin değilim

Her durumda, genellikle bir şey sızdığında, çok kez olur ve gerçek bir projede kullandığımda, sızıntı fonksiyonuna çok kolay bir şekilde işaret edildim.

Çıktının kendisinde belirtildiği gibi, bu önemli bir yürütme yavaşlamasına neden olur.

Daha fazla dokümantasyon:

Ayrıca bkz: TCMalloc Nasıl Kullanılır?

Ubuntu 19.04'te test edilmiştir, google-perftools 2.5-2.

Sanitizer (ASan) adresi ayrıca Google tarafından

https://github.com/google/sanitizers

Daha önce de belirtildiği gibi: C ++ kodunda / projesinde bellek sızıntısı nasıl bulunur? TODO ve tcmalloc karşılaştırması.

Bu zaten GCC'ye entegre edilmiştir, böylece şunları yapabilirsiniz:

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

ve yürütme çıktıları:

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

tüm sızıntıları açıkça tanımlar. Güzel!

ASan ayrıca sınır dışı yazma gibi diğer serin denetimleri de yapabilir: Yığın parçalama algılandı

Ubuntu 19.04, GCC 8.3.0'da test edilmiştir.

Valgrind

http://www.valgrind.org/

Daha önce bahsedilenler: https://stackoverflow.com/a/37661630/895245

Kullanımı:

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

Çıktı:

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

Böylece bir kez daha tüm sızıntılar tespit edildi.

Ayrıca bkz: Bellek sızıntılarını bulmak için valgrind'i nasıl kullanırım?

Ubuntu 19.04, valgrind 3.14.0'da test edilmiştir.


4

Visual Leak Detector (VLD) , Visual C ++ için ücretsiz, sağlam, açık kaynaklı bir bellek sızıntısı algılama sistemidir.

Programınızı Visual Studio hata ayıklayıcı altında çalıştırdığınızda, Visual Leak Detector hata ayıklama oturumunuzun sonunda bir bellek sızıntısı raporu verir. Sızıntı raporu, sızan bellek bloklarının nasıl tahsis edildiğini gösteren tam çağrı yığınını içerir . Düzenleyici penceresindeki o dosyaya ve satıra atlamak için çağrı yığınındaki bir satıra çift tıklayın.

Yalnızca çökme dökümleriniz varsa, Windbg !heap -lkomutunu kullanabilirsiniz, sızan yığın bloklarını algılar. Daha iyi gflags seçeneğini açın: “Kullanıcı modu yığın izleme veritabanı oluştur”, daha sonra bellek ayırma çağrısı yığınını göreceksiniz.


4

MTuner , MSVC, GCC ve Clang derleyicilerini destekleyen ücretsiz bir çoklu platform bellek profili çıkarma , sızıntı algılama ve analiz aracıdır. Özellikler:

  • zaman çizelgesi tabanlı bellek kullanımı geçmişi ve canlı bellek blokları
  • yığın, bellek etiketi, zaman aralığı vb.
  • Tam kaynak kodlu manuel enstrümantasyon için SDK
  • komut satırı kullanımı ile sürekli entegrasyon desteği
  • çağrı yığını ağaç ve ağaç harita navigasyon
  • daha fazla.

Kullanıcılar, GCC veya Clang çapraz derleyicileriyle herhangi bir yazılım hedefleme platformunu profilleyebilir. MTuner, Windows, PlayStation 4 ve PlayStation 3 platformları için yerleşik destekle birlikte gelir.


Bu kabul edilen cevap olmalı. Harika bir araçtır ve başkalarının yapamayacağı tahsis / anlaşma hacimlerini karşılayabilir.
Serge Rogatch

3

Windows'ta CRT hata ayıklama yığınını kullanabilirsiniz .

Programda bellek sızıntısı olmadığından emin olmak için izlenmesi gereken herhangi bir standart veya prosedür var mı?

Evet, manuel bellek yönetimi kullanmayın (daha önce deleteveya delete[]elle ararsanız , yanlış yaparsınız). RAII ve akıllı işaretçiler kullanın, yığın dağılımlarını mutlak minimumla sınırlayın (çoğu zaman otomatik değişkenler yeterli olacaktır).


3

Sorunuzun ikinci bölümünü yanıtlayarak,

Programda bellek sızıntısı olmadığından emin olmak için izlenmesi gereken herhangi bir standart veya prosedür var mı?

Evet var. Ve bu C ve C ++ arasındaki en önemli farklılıklardan biridir.

C ++ ' newda asla deletekullanıcı kodunuzu aramamalısınız. RAII , kaynak yönetimi sorununu hemen hemen çözen çok yaygın olarak kullanılan bir tekniktir. Programınızdaki her kaynak (bir kaynak edinilmesi gereken ve daha sonra yayımlanan her şeydir: dosya tanıtıcıları, ağ yuvaları, veritabanı bağlantıları, aynı zamanda düz bellek ayırmaları ve bazı durumlarda API çağrıları çiftleri (BeginX ( ) / EndX (), LockY (), UnlockY ()), bir sınıfa sarılmalıdır, burada:

  • yapıcı kaynağı edinir ( newkaynağın not ayırması olup olmadığını çağırarak )
  • yıkıcı kaynağı serbest bırakır ,
  • kopyalama ve atama önlenir (kopya oluşturucu ve atama işleçlerini özel yaparak) veya düzgün çalışmak üzere uygulanır (örneğin, temeldeki kaynağı klonlayarak)

Bu sınıf daha sonra yerel olarak, yığın üzerinde veya bir sınıf üyesi olarak başlatılır ve bir işaretçi çağrılarak ve saklanarak değilnew .

Bu sınıfları sıklıkla kendiniz tanımlamanız gerekmez. Standart kütüphane kapları da bu şekilde davranır, böylece bir std::vectorvektörde depolanan herhangi bir nesne vektör yok edildiğinde serbest kalır. Bu yüzden, tekrar, bir işaretçi kapsayıcısında saklamanızı ( arama yapmanızı gerektirir ) newve deletedaha ziyade nesnenin kendisini (size ücretsiz bellek yönetimi sağlar ). Benzer şekilde, akıllı işaretçi sınıfları, yalnızca tahsis edilmesi gereken nesneleri kolayca sarmak için kullanılabilirnew ve yaşam sürelerini kontrol etmek için kullanılabilir.

Bu, nesne kapsam dışına çıktığında, otomatik olarak yok edildiği ve kaynağının serbest bırakıldığı ve temizlendiği anlamına gelir.

Bunu kodunuz boyunca tutarlı bir şekilde yaparsanız, herhangi bir bellek sızıntısı olmaz. Sızan olabilecek her şey, kontrol nesnenin bildirildiği kapsamdan çıktığında çağrılması garanti edilen bir yıkıcıya bağlıdır.


akıllı işaretçi bir sınıfı tutuyorsa ve bu sınıf başka bir sınıfın işaretçisini içeriyorsa. akıllı kapandığında, içerideki tüm işaretçinin güvenli bir şekilde silineceği anlamına gelir.
Chris_vr

@Chris: Akıllı işaretçiyle gösterilen nesnenin , gerekli temizliği yapan bir yıkıcıya sahip olduğu veya nesnenin, gerekli temizliği gerçekleştirmek için yıkıcılara sahip olan üyeleri içerdiği varsayılarak . Her nesne bir işaretçi, serbest gereklilikleri o zaman her şey, değeri depolanır, özde, sürece her nesnenin kendisi ilgilenir sürece (yok edilir, kendisi sonra temizlik) ve olacak serbest olsun.
jalf

3

AddressSanitizer (ASan) bir hızlı bellek hata dedektörüdür. C / C ++ programlarında serbest kullanım ve {yığın, yığın, global} -buffer taşma hataları bulur. Bulur:

  • Serbest bırakıldıktan sonra kullan (sarkan işaretçi serbest bırakma)
  • Yığın arabelleği taşması
  • Yığın arabellek taşması
  • Global arabellek taşması
  • Dönüşten sonra kullanın
  • Başlatma sırası hataları

Bu araç çok hızlı. Enstrümanlı programın ortalama yavaşlaması ~ 2x.



0

Diğer yanıtlarda sağlanan araçlara ve yöntemlere ek olarak, bellek sızıntılarını (ve diğer sorunları da) tespit etmek için statik kod analiz araçları kullanılabilir. Ücretsiz sağlam bir araç Cppcheck'tir. Ancak başka birçok araç var. Wikipedia'nın statik kod analiz araçlarının bir listesi vardır.


-1

Tüm yığın belleğin başarıyla boşaltıldığından emin olun. Yığın üzerinde hiçbir zaman bellek ayırmanıza gerek yoktur. Bunu yaparsanız, belleği kaç kez ayırdığınızı sayın ve belleği boşalttığınız zamanı sayın.


-3

Uygulama kodunda hiçbir zaman "yeni" veya "sil" kullanılmamalıdır. Bunun yerine, yönetici sınıfının belleği ayırıp serbest bıraktığı ve diğer tüm işlemleri worker (işçi) nesnesine ilettiği manager / worker deyimini kullanan yeni bir tür oluşturun.

Ne yazık ki, C ++ 'operatörünün aşırı yüklenmesi olmadığından bu olması gerekenden daha fazla iştir. Polimorfizm varlığında daha da fazla çalışmadır.

Ama bu çabaya değer, çünkü o zaman asla bellek sızıntıları hakkında endişelenmenize gerek yok, yani onları aramak zorunda bile değilsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.