使用malloc_hook追踪内存时memory_bytes_hook出现负值的原因排查
malloc_hook追踪内存出现负值的原因及解决思路
你遇到的memory_bytes_hook变为负值的情况,核心是内存统计的收支不平衡——要么部分内存分配没被统计,要么释放操作被重复/错误统计,结合你的代码和malloc_hook特性,具体原因如下:
1. 未覆盖所有内存操作入口
你的代码只hook了malloc、free、realloc,但标准库还有其他内存操作函数没处理:
calloc:该函数分配内存并初始化,对应钩子是__calloc_hook,若程序调用calloc,你的代码不会统计这部分分配,但后续free时会减去对应大小,直接导致统计值减少。memalign/posix_memalign:这类对齐分配的函数也有专属钩子,被调用后会漏统计。- 部分第三方库自带内存管理(比如直接用
mmap申请内存),这类操作不会被malloc_hook捕获,若后续用free释放(不符合规范但可能发生),会错误减去不存在的分配量。
2. realloc特殊场景处理错误
当调用realloc(ptr, 0)时,标准库行为等价于free(ptr)并返回NULL。你的代码会先执行:
memory_bytes_hook += (0 - oldSize); // 这里已经减去了内存块大小
但realloc内部调用的free会触发你的my_free_hook,再次执行memory_bytes_hook -= oldSize,相当于同一个内存块被重复减两次,直接导致统计值过度减少。
3. 非法free操作导致错误统计
如果程序出现以下情况,统计逻辑会出错:
- 重复free同一个指针:每次free都会触发钩子,重复减去该内存块大小,多次操作后必然出现负值。
- free非malloc分配的内存:比如free栈上指针、mmap申请的内存,此时
malloc_usable_size(ptr)会返回随机值(甚至很大的数),减去后直接让统计值暴跌至负数。
4. malloc_hook被覆盖
malloc_hook是全局变量,若程序中其他模块(比如第三方库)也设置了自己的malloc_hook,会覆盖你的钩子:
- 如果分配操作被其他钩子处理而没被你的代码统计,但释放操作仍由你的钩子处理,就会出现“只减不增”的情况,最终导致负值。
对应的修复思路
- 补全所有内存操作钩子:添加
__calloc_hook、__memalign_hook等的处理函数,确保所有标准库内存分配都被统计。 - 修复realloc特殊场景:在
my_realloc_hook中判断size == 0的情况,直接调用free并更新统计,避免重复触发free_hook:static void* my_realloc_hook (void *ptr, size_t size, const void *caller) { const std::lock_guard<std::mutex> lock(memory_mutex_hook); __malloc_hook = nullptr; __free_hook = nullptr; __realloc_hook = nullptr; if (size == 0 && ptr != nullptr) { long int oldSize = malloc_usable_size(ptr); free(ptr); memory_bytes_hook -= oldSize; my_init_hook(); return nullptr; } long int oldSize = malloc_usable_size(ptr); void *result = realloc(ptr, size); long int updated_size = malloc_usable_size(result); memory_bytes_hook += (updated_size - oldSize); if(peak_memory_hook < memory_bytes_hook ) peak_memory_hook = memory_bytes_hook ; my_init_hook(); return result; } - 跟踪已分配指针:维护一个哈希表(比如
std::unordered_map),在分配钩子中记录指针和对应大小,在free钩子中先检查指针是否存在:- 存在则减去对应大小并移除记录
- 不存在则跳过统计(或打印日志排查非法free)
- 避免钩子被覆盖:改用
LD_PRELOAD机制加载自定义malloc库,这种方式优先级更高,能避免被其他模块覆盖钩子。
附原问题代码
long int memory_bytes_hook = 0; long int peak_memory_hook = 0; std::mutex memory_mutex_hook; static void my_init_hook (void); static void *my_malloc_hook (size_t, const void *); static void my_free_hook (void*, const void *); static void* my_realloc_hook (void *ptr, size_t size, const void *caller); static void *(*old_malloc_hook)(size_t, const void *); static void (*old_free_hook)(void*, const void *); static void *(*old_realloc_hook)(void *ptr, size_t size, const void *caller); static void my_init_hook (void) { __malloc_hook = my_malloc_hook; __free_hook = my_free_hook; __realloc_hook = my_realloc_hook ; } static void * my_malloc_hook (size_t size, const void *caller) { const std::lock_guard<std::mutex> lock(memory_mutex_hook); void *result; __malloc_hook = nullptr; __free_hook = nullptr ; __realloc_hook = nullptr ; result = malloc (size); memory_bytes_hook += malloc_usable_size(result); if(peak_memory_hook < memory_bytes_hook ) peak_memory_hook = memory_bytes_hook ; my_init_hook(); return result; } static void* my_realloc_hook (void *ptr, size_t size, const void *caller) { const std::lock_guard<std::mutex> lock(memory_mutex_hook); __malloc_hook = nullptr; __free_hook = nullptr;//old_free_hook ; __realloc_hook = nullptr;//old_realloc_hook ; long int oldSize = malloc_usable_size(ptr); void *result = realloc(ptr, size); long int updated_size = malloc_usable_size(result); memory_bytes_hook += (updated_size - oldSize); if(peak_memory_hook < memory_bytes_hook ) peak_memory_hook = memory_bytes_hook ; my_init_hook(); return result; } static void my_free_hook (void *ptr, const void *caller) { const std::lock_guard<std::mutex> lock(memory_mutex_hook); __malloc_hook = nullptr;// old_malloc_hook; __free_hook = nullptr;// old_free_hook ; __realloc_hook = nullptr;//old_realloc_hook ; /* Call recursively */ long int oldSize = malloc_usable_size(ptr); free (ptr); memory_bytes_hook -= oldSize; if(peak_memory_hook < memory_bytes_hook ) peak_memory_hook = memory_bytes_hook ; my_init_hook(); } int main(int argc, char** argv) { my_init_hook(); ... calling other libraries and do lot of operation for couple of hours... std::cout <<"At last: peak_memory hook: "<< peak_memory_hook << " " << " Bytes allocated: " << memory_bytes_hook << " "; return 0; }
内容的提问来源于stack exchange,提问作者Raj Kumar
相关产品推荐
相关产品推荐

