Aracılığıyla paylaş


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_dbgserbest bırakma işlemlerini izleyen hata ayıklama sürümleriyle _malloc_dbg eşlermalloc. Bu eşleme yalnızca içeren _DEBUGhata 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_ALLOCbellek 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_ALLOCda 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 newkullanı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ı newbir ö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

  1. Uygulamanızın başlangıcına yakın bir kesme noktası ayarlayın ve hata ayıklamaya başlayın.

  2. 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.

  3. 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.

  4. 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.

  5. 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 );

_CrtMemDifferenceve bellek durumlarını s1 s2 karşılaştırır ve ile arasındaki s2s1 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 _CrtDumpMemoryLeaksnoktadan 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.

Ayrıca bkz.