如何在程序中找到何處在不斷佔用內存
一個簡單而有效的方案:
分配的時候多分配一點空間作頭,記下分配的文件名和行號,把所有的頭連成一個環形雙
鏈表,釋放的時候從雙鏈表中刪除,並釋放頭。
結束的時候只要檢查這個雙鏈表是否唯恐就知道哪裏泄漏內存了
在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;
}
分配的時候多分配一點空間作頭,記下分配的文件名和行號,把所有的頭連成一個環形雙
鏈表,釋放的時候從雙鏈表中刪除,並釋放頭。
結束的時候只要檢查這個雙鏈表是否唯恐就知道哪裏泄漏內存了
在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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.