有的时候我们想要跟踪我们的代码到底分配了多少的内存,一个常用的方法是使用 Valgrind 工具进行内存分析;
但是对于一些场景,我们不想这么麻烦,那么此时我们可以通过简单的覆盖 malloc、free 等函数实现!
源代码:
简单实现C++内存分配跟踪
简单实现
我们知道,C、C++ 中主要是通过 malloc、free 函数以及 new、delete、new[]、delete[] 等运算符进行内存分配;
对于 malloc、free 函数而言:
虽然他们是在编译时通过链接到 glibc 库加入的,但是我们可以通过宏定义来替换他们,从而实现自己的逻辑;
对于 new、delete 等运算符,可以通过全局覆盖(override)他们来实现 Hook;
具体实现的代码很简单,如下:
1 |
|
为了使用起来更加方便,我定义了:
TRACK_MEMORY:启用内存监控的总开关;PRINT_MEMORY_TRACKING:是否打印内存分配内容;
同时,定义了:gAllocationMapStandard 和 gAllocationMapArray 来保存分配的内存空间;
并且使用 gAllocatedMemory 来记录当前已分配的字节数;
覆盖malloc 和 free
代码中,wrap_malloc 和 wrap_free 包装了在 glibc 中的 malloc 和 free 函数;
随后,通过宏覆盖了原来的 malloc 和 free:
1 |
注意:这里必须要先定义函数,再进行宏替换,否则会将 wrapper 函数中的 malloc 也给替换掉!
这里实现的是单线程的内存分配跟踪,对于多线程的实现,需要使用 Lock 将上面共享的内容保护起来!
覆盖new和delete
上面的代码完成了对 malloc 和 free 函数的覆盖;
那么对于 new和delete 等运算符来说,我们只需要调用上面已经实现的包装函数即可:
1 | void *operator new(size_t size) noexcept(false) { |
这里需要注意的是:
由于运算符重载函数是无法 inline 的,因此 __LINE__、__FILE__ 实际上只能获取到这个文件的路径;
并且,由于我们在重载时,无法实现在和原来的 void *operator new(size_t size) 函数签名完全相同下、增加入参的重载(否则会不知道究竟使用哪个实现!)
如果有小伙伴们知道怎么做,可以在下方留言评论~
关于重写库函数或系统调用:
至此,我们的功能编写完成!
测试用例
在使用时,我们在源代码的开头引入这个头文件,并定义对应的宏开启功能即可!
即:
1 |
例如:
1 |
|
执行后输出:
1 | [Malloc /root/data/cpp-learn/track_memory.h:94:operator new []] Allocated mem 0x01b0f260: 100 (100) |
即可完成对内存分配的跟踪,非常方便!
附录
源代码:
文章参考:
- http://joshcarter.com/software/easy_cpp_allocation_tracking/
- https://stackoverflow.com/questions/438515/how-to-track-memory-allocations-in-c-especially-new-delete
- https://irgb.github.io/%E5%A6%82%E4%BD%95%E9%87%8D%E5%86%99%E5%BA%93%E5%87%BD%E6%95%B0%E6%88%96%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8/
- https://stackoverflow.com/questions/1094532/problem-in-overriding-malloc