CRT kitaplığı ile bellek sızıntılarını bulma
Bellek sızıntıları, C/C++ uygulamalarındaki en ince ve algısı zor hatalar arasındadır. Bellek sızıntıları, daha önce ayrılmış olan belleğin doğru şekilde serbest bırakılamamasından kaynaklanır. İlk başta küçük bir bellek sızıntısı fark edilmeyebilir, ancak zaman içinde uygulama belleği yetersiz olduğunda düşük performanstan kilitlenmeye kadar çeşitli belirtilere neden olabilir. Tüm kullanılabilir belleği kullanan bir uygulama, diğer uygulamaların kilitlenmesine neden olabilir ve bu da uygulamanın sorumlu olduğu konusunda karışıklıklara neden olabilir. Zararsız bellek sızıntıları bile düzeltilmesi gereken diğer sorunları gösterebilir.
Visual Studio hata ayıklayıcısı ve C Çalışma Zamanı Kitaplığı (CRT), bellek sızıntılarını algılamanıza ve belirlemenize yardımcı olabilir.
Bellek sızıntısı algılamayı etkinleştirme
Bellek sızıntılarını algılamaya yönelik birincil araçlar C/C++ hata ayıklayıcısı ve CRT hata ayıklama yığın işlevleridir.
Tüm hata ayıklama yığını işlevlerini etkinleştirmek için C++ programınıza aşağıdaki deyimleri aşağıdaki sırayla ekleyin:
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
deyimi, #define
CRT yığın işlevlerinin temel sürümünü ilgili hata ayıklama sürümüne eşler. deyimini #define
atlarsanız bellek sızıntısı dökümü daha az ayrıntılı olur.
dahil olmak üzere crtdbg.h, ve free
işlevlerini, bellek ayırma ve _free_dbg
serbest bırakma işlemlerini izleyen hata ayıklama sürümleriyle _malloc_dbg
eşlermalloc
. Bu eşleme yalnızca içeren _DEBUG
hata ayıklama derlemelerinde gerçekleşir. Yayın derlemeleri sıradan malloc
ve free
işlevleri kullanır.
Yukarıdaki deyimleri kullanarak hata ayıklama yığını işlevlerini etkinleştirdikten sonra, uygulamadan çıkıldığında bellek sızıntısı raporu görüntülemek için bir uygulama çıkış noktasından önce öğesine bir çağrı _CrtDumpMemoryLeaks
yerleştirin.
_CrtDumpMemoryLeaks();
Uygulamanızın birkaç çıkışı varsa, her çıkış noktasına el ile yerleştirmeniz _CrtDumpMemoryLeaks
gerekmez. Her çıkış noktasında otomatik çağrıya neden olmak için _CrtDumpMemoryLeaks
_CrtSetDbgFlag
, burada gösterilen bit alanlarıyla uygulamanızın başına bir çağrı yerleştirin:
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
Varsayılan olarak, _CrtDumpMemoryLeaks
bellek sızıntısı raporunu Çıkış penceresinin Hata Ayıklama bölmesine aktarır . Kitaplık kullanıyorsanız, kitaplık çıkışı başka bir konuma sıfırlayabilir.
Raporu başka bir konuma yeniden yönlendirmek veya burada gösterildiği gibi Çıkış penceresine geri dönmek için kullanabilirsiniz_CrtSetReportMode
:
_CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_DEBUG );
Aşağıdaki örnek basit bir bellek sızıntısını gösterir ve kullanarak _CrtDumpMemoryLeaks();
bellek sızıntısı bilgilerini görüntüler.
// debug_malloc.cpp
// compile by using: cl /EHsc /W4 /D_DEBUG /MDd debug_malloc.cpp
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#include <iostream>
int main()
{
std::cout << "Hello World!\n";
int* x = (int*)malloc(sizeof(int));
*x = 7;
printf("%d\n", *x);
x = (int*)calloc(3, sizeof(int));
x[0] = 7;
x[1] = 77;
x[2] = 777;
printf("%d %d %d\n", x[0], x[1], x[2]);
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
_CrtDumpMemoryLeaks();
}
Bellek sızıntısı raporunu yorumlama
Uygulamanız öğesini tanımlamıyorsa _CRTDBG_MAP_ALLOC
_CrtDumpMemoryLeaks şuna benzer bir bellek sızıntısı raporu görüntüler:
Detected memory leaks!
Dumping objects ->
{18} normal block at 0x00780E80, 64 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
Uygulamanız öğesini tanımlıyorsa _CRTDBG_MAP_ALLOC
bellek sızıntısı raporu şöyle görünür:
Detected memory leaks!
Dumping objects ->
c:\users\username\documents\projects\leaktest\leaktest.cpp(20) : {18}
normal block at 0x00780E80, 64 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
İkinci rapor, sızdırılan belleğin ilk ayrıldığı dosya adını ve satır numarasını gösterir.
tanımlasanız da tanımlamasanız _CRTDBG_MAP_ALLOC
da bellek sızıntısı raporu görüntülenir:
- Örnekteki
18
bellek ayırma numarası - Örnekteki
normal
blok türü. - Örnekte onaltılık bellek konumu
0x00780E80
. - Örnekte bloğun
64 bytes
boyutu. - Bloktaki verilerin ilk 16 baytı onaltılık biçimdedir.
Bellek bloğu türleri normal, istemci veya CRT'dir. Normal blok, programınız tarafından ayrılan sıradan bellektir. İstemci bloğu, MFC programları tarafından yıkıcı gerektiren nesneler için kullanılan özel bir bellek bloğu türüdür. MFC new
işleci, oluşturulan nesneye uygun olarak normal bir blok veya istemci bloğu oluşturur.
CRT bloğu, CRT kitaplığı tarafından kendi kullanımı için ayrılır. CRT kitaplığı bu bloklar için serbest bırakma işlemini işler, bu nedenle CRT kitaplığında ciddi sorunlar olmadığı sürece CRT blokları bellek sızıntısı raporunda görünmez.
Bellek sızıntısı raporlarında hiçbir zaman görünmeyen iki tür daha bellek bloğu vardır. Boş blok, serbest bırakılmış bellektir, bu nedenle tanım gereği sızdırılamaz. Yoksay bloğu, bellek sızıntısı raporundan dışlamak üzere açıkça işaretlediğiniz bellektir.
Yukarıdaki teknikler, standart CRT malloc
işlevi kullanılarak ayrılan bellek için bellek sızıntılarını tanımlar. Ancak programınız C++ new
işlecini kullanarak bellek ayırıyorsa, yalnızca bellek sızıntısı raporunda çağrıların _malloc_dbg
bulunduğu operator new
dosya adını ve satır numarasını görebilirsiniz. Daha kullanışlı bir bellek sızıntısı raporu oluşturmak için, ayırmayı yapan satırı raporlamak için aşağıdaki gibi bir makro yazabilirsiniz:
#ifdef _DEBUG
#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
// Replace _NORMAL_BLOCK with _CLIENT_BLOCK if you want the
// allocations to be of _CLIENT_BLOCK type
#else
#define DBG_NEW new
#endif
Artık kodunuzdaki makroyu new
kullanarak işlecini DBG_NEW
değiştirebilirsiniz. Hata ayıklama derlemelerinde blok türü, DBG_NEW
dosya ve satır numarası için ek parametreler alan genel bir aşırı yükleme operator new
kullanır. Ek bilgileri kaydetmek için çağrıların new
_malloc_dbg
aşırı yüklenmesi. Bellek sızıntısı raporları, sızdırılan nesnelerin ayrıldığı dosya adını ve satır numarasını gösterir. Yayın derlemeleri hala varsayılan new
kullanır. Tekniğin bir örneği aşağıda verilmiştir:
// debug_new.cpp
// compile by using: cl /EHsc /W4 /D_DEBUG /MDd debug_new.cpp
#define _CRTDBG_MAP_ALLOC
#include <cstdlib>
#include <crtdbg.h>
#ifdef _DEBUG
#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
// Replace _NORMAL_BLOCK with _CLIENT_BLOCK if you want the
// allocations to be of _CLIENT_BLOCK type
#else
#define DBG_NEW new
#endif
struct Pod {
int x;
};
void main() {
Pod* pPod = DBG_NEW Pod;
pPod = DBG_NEW Pod; // Oops, leaked the original pPod!
delete pPod;
_CrtDumpMemoryLeaks();
}
Bu kodu Visual Studio hata ayıklayıcısında çalıştırdığınızda, çağrısı _CrtDumpMemoryLeaks
Çıkış penceresinde şuna benzer bir rapor oluşturur:
Detected memory leaks!
Dumping objects ->
c:\users\username\documents\projects\debug_new\debug_new.cpp(20) : {75}
normal block at 0x0098B8C8, 4 bytes long.
Data: < > CD CD CD CD
Object dump complete.
Bu çıkış, sızdırılan ayırmanın debug_new.cpp 20. satırında olduğunu bildirir.
Not
adlı new
bir önişlemci makro veya başka bir dil anahtar sözcüğü oluşturmanızı önermeyiz.
Bellek ayırma numarasında kesme noktaları ayarlama
Bellek ayırma numarası, sızdırılan bir bellek bloğunun ne zaman ayrıldığını bildirir. Örneğin, 18 bellek ayırma numarasına sahip bir blok, uygulamanın çalıştırılması sırasında ayrılan 18. bellek bloğudur. CRT raporu, CRT kitaplığı ve MFC gibi diğer kitaplıklar tarafından yapılan ayırmalar dahil olmak üzere çalıştırma sırasında tüm bellek bloğu ayırmalarını sayar. Bu nedenle, 18 numaralı bellek ayırma bloğu büyük olasılıkla kodunuz tarafından ayrılan 18. bellek bloğu değildir.
Bellek ayırmada bir kesme noktası ayarlamak için ayırma numarasını kullanabilirsiniz.
İzleme penceresini kullanarak bellek ayırma kesme noktası ayarlamak için
Uygulamanızın başlangıcına yakın bir kesme noktası ayarlayın ve hata ayıklamaya başlayın.
Uygulama kesme noktasında duraklatıldığında, Windows>Watch 1'de Hata Ayıkla>(veya İzleme 2, İzleme 3 veya 4 İzle) seçeneğini belirleyerek bir Gözcü penceresi açın.
Gözcü penceresinde Ad sütununa yazın
_crtBreakAlloc
.CRT kitaplığının çok iş parçacıklı DLL sürümünü (/MD seçeneği) kullanıyorsanız bağlam işlecini ekleyin:
{,,ucrtbased.dll}_crtBreakAlloc
Hata ayıklama simgelerinin yüklendiğinden emin olun. Aksi takdirde,
_crtBreakAlloc
tanımlanmamış olarak bildirilir.Enter'a basın.
Hata ayıklayıcı çağrıyı değerlendirir ve sonucu Değer sütununa yerleştirir. Bellek ayırmalarında kesme noktası ayarlamadıysanız bu değer -1 olur.
Değer sütununda değeri, hata ayıklayıcının kesmesini istediğiniz bellek ayırmasının ayırma numarasıyla değiştirin.
Bellek ayırma numarasında kesme noktası ayarladıktan sonra hata ayıklamaya devam edin. Bellek ayırma numarasının değişmemesi için aynı koşullar altında çalıştırdığınızdan emin olun. Programınız belirtilen bellek ayırma sırasında sonlandığında, belleğin hangi koşullarda ayrıldığını belirlemek için Çağrı Yığını penceresini ve diğer hata ayıklayıcı pencerelerini kullanın. Ardından, nesneye ne olduğunu gözlemlemek ve neden doğru şekilde serbest bırakılmadığını belirlemek için yürütmeye devam edebilirsiniz.
Nesne üzerinde bir veri kesme noktası ayarlamak da yararlı olabilir. Daha fazla bilgi için bkz . Kesme noktalarını kullanma.
Ayrıca kodda bellek ayırma kesme noktaları da ayarlayabilirsiniz. Şunu ayarlayabilirsiniz:
_crtBreakAlloc = 18;
veya:
_CrtSetBreakAlloc(18);
Bellek durumlarını karşılaştırma
Bellek sızıntılarını bulmak için kullanılan bir diğer teknik de uygulamanın bellek durumunun önemli noktalarda anlık görüntülerinin alınmasıdır. Uygulamanızın belirli bir noktasında bellek durumunun anlık görüntüsünü almak için bir _CrtMemState
yapı oluşturun ve bunu işleve _CrtMemCheckpoint
geçirin.
_CrtMemState s1;
_CrtMemCheckpoint( &s1 );
işlevi, _CrtMemCheckpoint
yapıyı geçerli bellek durumunun anlık görüntüsüyle doldurur.
Bir _CrtMemState
yapının içeriğini çıkarmak için yapıyı işleve _ CrtMemDumpStatistics
geçirin:
_CrtMemDumpStatistics( &s1 );
_CrtMemDumpStatistics
şuna benzer bir bellek durumu dökümü oluşturur:
0 bytes in 0 Free Blocks.
0 bytes in 0 Normal Blocks.
3071 bytes in 16 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 3071 bytes.
Total allocations: 3764 bytes.
Kodun bir bölümünde bellek sızıntısı olup olmadığını belirlemek için, bölümden önce ve sonra bellek durumunun anlık görüntülerini alabilir ve ardından iki durumu karşılaştırmak için kullanabilirsiniz _CrtMemDifference
:
_CrtMemCheckpoint( &s1 );
// memory allocations take place here
_CrtMemCheckpoint( &s2 );
if ( _CrtMemDifference( &s3, &s1, &s2) )
_CrtMemDumpStatistics( &s3 );
_CrtMemDifference
ve bellek durumlarını s1
s2
karşılaştırır ve ile arasındaki s2
s1
fark olan (s3
) sonucunu döndürür.
Bellek sızıntılarını bulma tekniklerinden biri, uygulamanızın başına ve sonuna çağrılar yerleştirerek _CrtMemCheckpoint
ve ardından sonuçları karşılaştırmak için kullanarak _CrtMemDifference
başlar. Bellek sızıntısı gösteriyorsa _CrtMemDifference
, sızıntının kaynağını yalıtana kadar ikili arama kullanarak programınızı bölmek için daha fazla _CrtMemCheckpoint
çağrı ekleyebilirsiniz.
Hatalı pozitifler
_CrtDumpMemoryLeaks
bir kitaplık iç ayırmaları CRT blokları veya istemci blokları yerine normal bloklar olarak işaretlerse, bellek sızıntılarına yönelik yanlış göstergeler verebilir. Bu durumda, _CrtDumpMemoryLeaks
kullanıcı ayırmaları ile iç kitaplık ayırmaları arasındaki farkı ayırt edemez. Kitaplık ayırmaları için genel yıkıcılar çağırdığınız _CrtDumpMemoryLeaks
noktadan sonra çalıştırılırsa, her iç kitaplık ayırması bir bellek sızıntısı olarak bildirilir. Standart Şablon Kitaplığı'nın Visual Studio .NET'ten önceki sürümleri bu tür hatalı pozitiflerin rapor edilmesine neden _CrtDumpMemoryLeaks
olabilir.