参考以下代码
#include <cassert>
#include <vector>
#include <dlfcn.h>
#include <limits>
#include <map>
#include <algorithm>
#include <iostream>
using std::cout;
using std::endl;
using std::vector;
/*
* Overload the malloc call
*/
int max_heap_usage = 0;
std::map<uintptr_t, int>& heap_memory_map;
void track_max_usage(std::map<uintptr_t, int> heap_memory_map,
int& max_heap_usage);
void* malloc(size_t size) {
// get the original malloc function call
static auto original_malloc = (decltype(&malloc)) dlsym(RTLD_NEXT, "malloc");
// Get the pointer from malloc
void* pointer = original_malloc(size);
uintptr_t pointer_handle = reinterpret_cast<uintptr_t>(pointer);
// assert that the pointer does not already exist in the memory map
assert("memory should not exist in memory map before allocation" &&
heap_memory_map.find(pointer_handle) == heap_memory_map.end());
// add to bookkeeping
heap_memory_map[pointer_handle] = size;
track_max_usage(heap_memory_map, max_heap_usage);
return pointer;
}
void* calloc(size_t count, size_t size) {
// get the original calloc
static auto original_calloc = (decltype(&calloc)) dlsym(RTLD_NEXT, "calloc");
// get the pointer returned by calloc
void* pointer = original_calloc(count, size);
uintptr_t pointer_handle = reinterpret_cast<uintptr_t>(pointer);
// assert that the memory has not been allocated before
assert("memory should not exist in the memory map before allocation" &&
heap_memory_map.find(pointer_handle) == heap_memory_map.end());
// add to bookkeeping
heap_memory_map[pointer_handle] = size * count;
track_max_usage(heap_memory_map, max_heap_usage);
return pointer;
}
void free(void* ptr) {
// get the original free function
static auto original_free = (decltype(&free)) dlsym(RTLD_NEXT, "free");
uintptr_t pointer_handle = reinterpret_cast<uintptr_t>(ptr);
// assert that the heap memory map already has the pointer
assert("memory to be freed does not exist in the heap memory map" &&
heap_memory_map.find(pointer_handle) != heap_memory_map.end());
// add to bookkeeping
heap_memory_map.erase(pointer_handle);
// free the memory
original_free(ptr);
}
/*
* Inputs: A map containing pointer values and the amount of heap memory used
* after that point
*
* The variable that keeps track of the max memory usage till this
* point
*
* This function updates the variable to have the max value if the current
* memory map dictates that the memory usage is greater than what it was
* before.
*/
void track_max_usage(std::map<uintptr_t, int>& heap_memory_map,
int& max_heap_usage) {
// loop through all keys and add up the values
int sum {0};
for (const auto ele : heap_memory_map) { sum += ele.second; }
// assign to max
max_heap_usage = std::max(max_heap_usage, sum);
}
int main() {
vector<int> vec {1, 2, 3, 4};
for (auto ele : vec) {
cout << ele << endl;
}
cout << "Total heap usage " << max_heap_usage << endl;
return 0;
}
我试图覆盖malloc,calloc和free调用,这样只要有堆分配,我就可以跟踪它。不知何故,vector类似乎没有在堆上分配任何内存。有人可以解释一下究竟发生了什么吗?我怎样才能达到预期的效果?
谢谢!
答案 0 :(得分:5)
您发布的程序 - 称之为main.cpp
- 编译不完整,因此无法完成
你想解释的令人失望的行为的程序:
error: 'heap_memory_map' declared as reference but not initialized
std::map<uintptr_t, int>& heap_memory_map;
^
如果我们通过声明来解决这个问题:
std::map<uintptr_t, int> heap_memory_map;
然后我们有一个链接错误:
undefined reference to `track_max_usage(std::map<unsigned long, int, std::less<unsigned long>, std::allocator<std::pair<unsigned long const, int> > >, int&)'
因为声明:
void track_max_usage(std::map<uintptr_t, int> heap_memory_map,
int& max_heap_usage);
与定义不符:
void track_max_usage(std::map<uintptr_t, int>& heap_memory_map,
int& max_heap_usage) {
...
}
如果我们通过声明来解决这个问题:
void track_max_usage(std::map<uintptr_t, int>& heap_memory_map,
int& max_heap_usage);
然后我们成功编译和链接,至少如果我们不挑剔 关于标准一致性:
$ g++ -o prog -std=c++11 -Wall main.cpp -ldl
如果我们对标准一致性非常挑剔:
$ g++ -o prog -std=c++11 -Wall -pedantic main.cpp -ldl
然后仍然存在编译错误:
main.cpp:20:25: error: declaration of ‘void* malloc(size_t)’ has a different exception specifier
void* malloc(size_t size) {
^
...
/usr/include/stdlib.h:466:14: error: from previous declaration ‘void* malloc(size_t) throw ()’
extern void *malloc (size_t __size) __THROW __attribute_malloc__ __wur;
^
main.cpp: In function ‘void* calloc(size_t, size_t)’:
main.cpp:40:39: error: declaration of ‘void* calloc(size_t, size_t)’ has a different exception specifier
void* calloc(size_t count, size_t size) {
^
...
/usr/include/stdlib.h:468:14: error: from previous declaration ‘void* calloc(size_t, size_t) throw ()’
extern void *calloc (size_t __nmemb, size_t __size)
^
main.cpp: In function ‘void free(void*)’:
main.cpp:60:20: error: declaration of ‘void free(void*)’ has a different exception specifier
void free(void* ptr) {
^
...
/usr/include/stdlib.h:483:13: error: from previous declaration ‘void free(void*) throw ()’
extern void free (void *__ptr) __THROW;
另外几只鲤鱼:
int
并不保证存储堆块的大小。因此标准库说:
void* malloc(size_t size);
void* calloc(size_t num, size_t size);
而不是:
void* malloc(int size);
void* calloc(int num, int size);
因此,您拥有以下权利:
size_t max_heap_usage = 0;
std::map<uintptr_t, size_t> heap_memory_map;
此外,您真正想要的是void *
的地图 - 值到大小,
并且没有理由不拥有这样的地图:
std::map<void *, size_t> heap_memory_map;
然后克制:
uintptr_t pointer_handle = reinterpret_cast<uintptr_t>(pointer);
可以免除。
继续我们所拥有的东西(并记住我们没有
确切地知道你已经得到了什么)运行prog
并不简单
没有统计任何堆分配;它崩溃了:
$ ./prog
Segmentation fault (core dumped)
如果您对此进行调试并仔细阅读了段跟踪的回溯,那么您将会这样做 看循环调用序列:
operator new(unsigned long)
__gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<unsigned long const, int> > >::allocate /usr/include/c++/5/ext/new_allocator.h 104
std::allocator_traits<std::allocator<std::_Rb_tree_node<std::pair<unsigned long const, int> > > >::allocate /usr/include/c++/5/bits/alloc_traits.h 360
std::_Rb_tree<unsigned long, std::pair<unsigned long const, int>, std::_Select1st<std::pair<unsigned long const, int> >, std::less<unsigned long>, std::allocator<std::pair<unsigned long const, int> > >::_M_get_node /usr/include/c++/5/bits/stl_tree.h 491
std::_Rb_tree<unsigned long, std::pair<unsigned long const, int>, std::_Select1st<std::pair<unsigned long const, int> >, std::less<unsigned long>, std::allocator<std::pair<unsigned long const, int> > >::_M_create_node<std::piecewise_construct_t const&, std::tuple<unsigned long const&>, std::tuple<> >(std::piecewise_construct_t const&, std::tuple<unsigned long const&>&&, std::tuple<>&&) /usr/include/c++/5/bits/stl_tree.h 545
std::_Rb_tree<unsigned long, std::pair<unsigned long const, int>, std::_Select1st<std::pair<unsigned long const, int> >, std::less<unsigned long>, std::allocator<std::pair<unsigned long const, int> > >::_M_emplace_hint_unique<std::piecewise_construct_t const&, std::tuple<unsigned long const&>, std::tuple<> >(std::_Rb_tree_const_iterator<std::pair<unsigned long const, int> >, std::piecewise_construct_t const&, std::tuple<unsigned long const&>&&, std::tuple<>&&) /usr/include/c++/5/bits/stl_tree.h 2170
std::map<unsigned long, int, std::less<unsigned long>, std::allocator<std::pair<unsigned long const, int> > >::operator[] /usr/include/c++/5/bits/stl_map.h 483
malloc /home/imk/develop/so/heap_track_orig/main.cpp 34
operator new(unsigned long)
重复 ad adause 。所以程序循环直到它用完了堆栈。
这是由于致命的逻辑缺陷造成的。您将继续假设所有C ++动态内存管理
程序中的操作将委托给标准C库设施
malloc
,calloc
和free
。
嗯,至少其中一些是,特别是对operator new
的调用
起源于
heap_memory_map[pointer_handle] = size;
当您分配堆映射的新元素时,委派给malloc
。
哪个是你的 malloc
。再次呼吁:
heap_memory_map[pointer_handle] = size;
然后operator new
,然后回到malloc
,依此类推到堆栈耗尽。
这是致命的逻辑缺陷,但激励性的假设也是不稳定的。
C ++标准甚至不需要operator new
和operator delete
的默认实现
malloc
分别委托free
和malloc/free
。它没有
指定C ++中的动态内存管理与C的动态内存管理之间的任何关系
我在这里使用的C ++编译器(Linux,GCC)确实如此委托,和
您可能也是如此,但实施者可能会选择委托两者
new/delete
和massif
直接与OS API。
不要尝试滚动自己的堆分析。使用适当的堆分析器。
对于linux,首选堆分析器是Valgrind's massif
。
您的发行版几乎肯定会提供Valgrind包,包括massif
。
这是我要用#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> vec;
for (int i = 0; i < 1000; ++i) {
vec.push_back(i);
}
for ( ;vec.size(); vec.pop_back()) {}
return 0;
}
进行分析的程序,并检查它的最大堆使用情况:
<强>的main.cpp 强>
$ g++ -g -o prog -Wall main.cpp
编译和链接:
valgrind
使用massif
运行$ valgrind --tool=massif ./prog
==6479== Massif, a heap profiler
==6479== Copyright (C) 2003-2015, and GNU GPL'd, by Nicholas Nethercote
==6479== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==6479== Command: ./prog
==6479==
==6479==
:
massif.out.NNNN
默认情况下,{ - 1}}会输出堆配置文件。我发现massif.out.6479
并运行:
$ ms_print massif.out.6479 > heap_prof.txt
我查看heap_prof.txt
并在第32行读到:
Number of snapshots: 29
Detailed snapshots: [4, 14, 17, 20, 23, 26 (peak)]
告诉我堆快照#26显示了峰值使用情况。我滚动到 快照#26,见:
--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
24 2,049,029 74,768 74,752 16 0
25 2,069,629 78,872 78,848 24 0
26 2,070,679 78,872 78,848 24 0
99.97% (78,848B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->92.18% (72,704B) 0x4EB91FE: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
| ->92.18% (72,704B) 0x4010608: call_init.part.0 (dl-init.c:72)
| ->92.18% (72,704B) 0x4010719: _dl_init (dl-init.c:30)
| ->92.18% (72,704B) 0x4000D08: ??? (in /lib/x86_64-linux-gnu/ld-2.21.so)
|
->07.79% (6,144B) 0x401788: __gnu_cxx::new_allocator<int>::allocate(unsigned long, void const*) (new_allocator.h:104)
->07.79% (6,144B) 0x401665: __gnu_cxx::__alloc_traits<std::allocator<int> >::allocate(std::allocator<int>&, unsigned long) (alloc_traits.h:182)
->07.79% (6,144B) 0x4014B0: std::_Vector_base<int, std::allocator<int> >::_M_allocate(unsigned long) (stl_vector.h:170)
->07.79% (6,144B) 0x400F59: std::vector<int, std::allocator<int> >::_M_insert_aux(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, int const&) (vector.tcc:353)
->07.79% (6,144B) 0x400CC4: std::vector<int, std::allocator<int> >::push_back(int const&) (stl_vector.h:925)
->07.79% (6,144B) 0x400AEC: main (main.cpp:9)
因此该程序的最高记录堆消耗为78,872字节
为我的std::vector
分配了(仅仅)6,144个字节。
答案 1 :(得分:3)
C ++标准库容器不使用malloc
等直接使用&#34; allocator&#34;对象,通常作为模板参数提供。您可以查看提供自定义分配器,或者提供自定义operator new
功能,如果您想为了测量目的而挂钩这样的东西。