如何在程序中找到何處在不斷佔用內存

一個簡單而有效的方案:

分配的時候多分配一點空間作頭,記下分配的文件名和行號,把所有的頭連成一個環形雙
鏈表,釋放的時候從雙鏈表中刪除,並釋放頭。

結束的時候只要檢查這個雙鏈表是否唯恐就知道哪裏泄漏內存了

在Windows DDK裏的簡化代碼:

in xxx.h

// the real heap function
void* sys_malloc(size_t size);
void sys_free(void* p);

//////////////////////////////////////////////////////////////////////////
// Heap tracking mechanism
#if DBG
void StartHeapTracking(void);
void StopHeapTracking(void);

void* sys_malloc_track(size_t size, const char* File, unsigned int Line);
void sys_free_track(void* mem);

// these macros will cover real heap functions in debug version
#define sys_malloc(size) sys_malloc_track(size, __FILE__, __LINE__)
#define sys_free    sys_free_track
#else
#define StartHeapTracking()
#define StopHeapTracking()
#endif

in xxx_dbg.c
//////////////////////////////////////////////////////////////////////////
// Heap tracking
//////////////////////////////////////////////////////////////////////////

#if DBG
struct HEAP_TRACKING_RECORD
{
    LIST_ENTRY Link;
    size_t Size;
    const char* File;
    unsigned int Line;
};

// the doubly linked list header
static LIST_ENTRY HeapTrack;
static KSPIN_LOCK HeapTrackLock;

void StartHeapTracking(void)
{
    InitializeListHead(&HeapTrack);
    KeInitializeSpinLock(&HeapTrackLock);
}

void StopHeapTracking(void)
{
    // check memory leak
    if(!IsListEmpty(&HeapTrack))
    {
        LIST_ENTRY* p;
        ASSERT(FALSE); // break here if memory leak
        for(p=HeapTrack.Flink; p!=&HeapTrack; p=p->Flink)
        {
            struct HEAP_TRACKING_RECORD* q = 
                CONTAINING_RECORD(p, struct HEAP_TRACKING_RECORD, Link);
            DbgPrint(
                "Memory leak: %u bytes allocated in %s:%u./n", 
                (unsigned)q->Size, 
                q->File,
                q->Line
            );
        }
    }
}

// following code using real heap function
#undef sys_malloc
#undef sys_free

void* sys_malloc_track(size_t size, const char* File, unsigned int Line)
{
    struct HEAP_TRACKING_RECORD* mem = 
        sys_malloc(sizeof(struct HEAP_TRACKING_RECORD)+size);
    if(mem)
    {
        mem->Size = size;
        mem->File = File;
        mem->Line = Line;
        ExInterlockedInsertTailList(&HeapTrack, &mem->Link, &HeapTrackLock);
        return mem+1;
    }
    return NULL;
}

void sys_free_track(void* mem)
{
    if(mem)
    {
        struct HEAP_TRACKING_RECORD* p = 
            (struct HEAP_TRACKING_RECORD*)
            ((char*)mem-sizeof(struct HEAP_TRACKING_RECORD));

        RemoveEntryList(&p->Link);
        sys_free(p);
    }
}

#endif

使用的時候包含xxx.h, 
程序開始的時候先調用StartHeapTracking初始化,然後直接用sys_malloc, sys_free
分配和釋放即可,結束的時候調用StopHeapTracking,就會把泄漏的內存塊分配的地方
大小打印出來。這樣在調試版每次分配要多耗費20字節的內存。如果不定義DBG預處理
符號,則等於用原來的sys_malloc和sys_free,不增加開銷。

代碼中用了Windows DDK中的雙鏈表函數,其他操作系統下比如Linux有類似的實現。
沒有的話自己寫也很簡單。

代碼是驅動程序中的一部分,其中的spinlock是爲了保護共享資源。
如果你的程序是單線程的,則可以不用加鎖。

如果你沒有DDK, 那麼可以參考這些雙鏈表函數相關的代碼:

typedef struct _LIST_ENTRY {
   struct _LIST_ENTRY *Flink;
   struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;

VOID
FORCEINLINE
InitializeListHead(
    IN PLIST_ENTRY ListHead
    )
{
    ListHead->Flink = ListHead->Blink = ListHead;
}

//
//  BOOLEAN
//  IsListEmpty(
//      PLIST_ENTRY ListHead
//      );
//

#define IsListEmpty(ListHead) /
    ((ListHead)->Flink == (ListHead))



VOID
FORCEINLINE
RemoveEntryList(
    IN PLIST_ENTRY Entry
    )
{
    PLIST_ENTRY Blink;
    PLIST_ENTRY Flink;

    Flink = Entry->Flink;
    Blink = Entry->Blink;
    Blink->Flink = Flink;
    Flink->Blink = Blink;
}

PLIST_ENTRY
FORCEINLINE
RemoveHeadList(
    IN PLIST_ENTRY ListHead
    )
{
    PLIST_ENTRY Flink;
    PLIST_ENTRY Entry;

    Entry = ListHead->Flink;
    Flink = Entry->Flink;
    ListHead->Flink = Flink;
    Flink->Blink = ListHead;
    return Entry;
}



PLIST_ENTRY
FORCEINLINE
RemoveTailList(
    IN PLIST_ENTRY ListHead
    )
{
    PLIST_ENTRY Blink;
    PLIST_ENTRY Entry;

    Entry = ListHead->Blink;
    Blink = Entry->Blink;
    ListHead->Blink = Blink;
    Blink->Flink = ListHead;
    return Entry;
}


VOID
FORCEINLINE
InsertTailList(
    IN PLIST_ENTRY ListHead,
    IN PLIST_ENTRY Entry
    )
{
    PLIST_ENTRY Blink;

    Blink = ListHead->Blink;
    Entry->Flink = ListHead;
    Entry->Blink = Blink;
    Blink->Flink = Entry;
    ListHead->Blink = Entry;
}


VOID
FORCEINLINE
InsertHeadList(
    IN PLIST_ENTRY ListHead,
    IN PLIST_ENTRY Entry
    )
{
    PLIST_ENTRY Flink;

    Flink = ListHead->Flink;
    Entry->Flink = Flink;
    Entry->Blink = ListHead;
    Flink->Blink = Entry;
    ListHead->Flink = Entry;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章