使用ltrace追踪时,std::string分配1KB内存的malloc/new调用在哪?
测试代码
#include <thread> #include <cstdlib> #include <cstdio> #include <unistd.h> #include <string> #include <iostream> int main() { std::string buf(1024, 'a'); std::cout << buf << '\0'; return 0; }
编译执行命令
g++ -O0 main.cpp && ltrace -n3 -fS ./a.out
ltrace输出
[pid 25689] SYS_mprotect(0x7f02e5293000, 4096, 1, 0x7f02e5294030) = 0 [pid 25689] SYS_brk(0, 0x7f02e46d3b20, 0x7f02e46d3b78, 0x7f02e46d3b78) = 0xb00000 [pid 25689] SYS_brk(0xb33000, 0x7f02e46d3b20, 0xb00000, 0x7f02e46d3b78) = 0xb33000 [pid 25689] __libc_start_main(0x400a22, 1, 0x7ffeeb3a1338, 0x400b80 <unfinished ...> [pid 25689] std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_local_data()(0x7ffeeb3a1210, 1024, 97, 0x7ffeeb3a1237) = 0x7ffeeb3a1220 [pid 25689] std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Alloc_hider::_Alloc_hider(char*, std::allocator<char> const&)(0x7ffeeb3a1210, 0x7ffeeb3a1220, 0x7ffeeb3a1237, 0x7ffeeb3a1220) = 0x7ffeeb3a1237 [pid 25689] std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct(unsigned long, char)(0x7ffeeb3a1210, 1024, 97, 1024) = 1024 [pid 25689] std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)(0x6012c0, 0x7ffeeb3a1210, 0xb12400, 0xb12400 <unfinished ...> [pid 25689] SYS_fstat(1, 0x7ffeeb3a1040, 0x7ffeeb3a1040, 1) = 0 [pid 25689] <... std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) resumed> ) = 0x6012c0 [pid 25689] std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char)(0x6012c0, 0, 0x7f02e5064310, 0) = 0x6012c0 [pid 25689] std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()(0x7ffeeb3a1210, 0x7f02e46d5780, 0x7f02e5064310, 0xb12831) = 1 [pid 25689] SYS_write(1, 0xb12430, 1025, 88aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) = 1025 [pid 25689] SYS_exit_group(0, 60, 0, 2956 <no return ...> [pid 25689] +++ exited (status 0) +++
疑问
分配1KB char内存的malloc和/或new调用在哪里?我曾怀疑它是在某个库调用(如_M_construct)内部执行而未被显示,但这个假设不成立,因为我能看到库调用内部的其他库调用。那么malloc/new调用究竟在哪里?我本以为会在std::string的构造函数中看到它。
注意:我知道string采用短缓冲区优化,但gcc中默认构造的std::string的本地容量为15(std::string str; std::cout << str.capacity() << '\n';),远小于1KB字符。因此这1KB的'a'必须被分配在堆上。
解答
核心原因
ltrace默认不会主动跟踪C++的operator new/operator delete,也不会自动展开std::allocator到malloc的调用链。你看到的SYS_brk是glibc内存分配器(ptmalloc)调整堆空间的系统调用,但ltrace默认只显示系统调用,没把上层的malloc调用展示出来。
验证方法
要看到malloc或operator new的调用,需要给ltrace添加指定跟踪函数的参数:
- 跟踪malloc调用:
ltrace -n3 -fS -e malloc ./a.out
- 跟踪所有库函数(包括malloc和operator new):
ltrace -n3 -f ./a.out
执行后就能看到std::string构造过程中触发的malloc/operator new调用了。
补充说明
std::string的内存分配是通过默认的std::allocator<char>完成的,allocator底层会调用malloc,但ltrace默认仅跟踪标准C库的公开函数和系统调用,不会自动关联allocator的内部调用。加上-e malloc参数后,ltrace会专门捕获malloc的调用,就能看到你预期的堆内存分配操作了。
内容的提问来源于stack exchange,提问作者ABu

