多线程程序中strace未捕获malloc/free系统调用的原因排查
为什么strace没捕获到C++程序的malloc/free系统调用?
我编写了一段C++多线程代码模拟内存泄漏问题:代码中通过new/delete创建和释放SharedObject对象,奇数迭代时不释放对象以模拟泄漏。该程序在Linux soumajit-HP-Pavilion-Desktop-590-p0xxx 5.4.0-150-generic #167~18.04.1-Ubuntu系统上编译运行。之后我尝试用strace跟踪malloc和free系统调用,但输出中仅显示write、futex、nanosleep等系统调用,未捕获到内存分配与释放的调用,请问原因是什么?
附代码
#include <iostream> #include <thread> #include <chrono> #include <mutex> #include <ctime> class SharedObject { public: std::string currentTime; SharedObject() { // Capture current time as string auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); currentTime = std::ctime(&now); // Convert time to string } void displayTime() { std::cout << "Current Time: " << currentTime; } }; void threadFunction(const std::string& threadName, int totalIterations) { for (int iteration = 1; iteration <= totalIterations; ++iteration) { SharedObject* obj = new SharedObject(); // Create object std::cout << threadName << " created an object at iteration " << iteration << std::endl; obj->displayTime(); // Every 100 iterations, forget to delete the object (simulating memory leak) if (iteration % 2 == 1) { std::cout << threadName << " forgot to delete the object at iteration " << iteration << std::endl; } else { delete obj; // Delete object std::cout << threadName << " deleted the object at iteration " << iteration << std::endl; } // Sleep for 512 milliseconds std::this_thread::sleep_for(std::chrono::milliseconds(512)); } std::cout << threadName << " completed all iterations.\n"; } int main() { const int totalIterations = 100000; // Launch two threads std::thread thread1(threadFunction, "Thread 1", totalIterations); std::thread thread2(threadFunction, "Thread 2", totalIterations); // Wait for threads to finish thread1.join(); thread2.join(); std::cout << "Both threads completed execution.\n"; return 0; }
附strace输出片段
[pid 18031] <... stat resumed> {st_mode=S_IFREG|0644, st_size=312, ...}) = 0 [pid 18032] write(1, "Current Time: Tue Jan 14 23:08:3"..., 39) = 39 [pid 18031] futex(0x7fcf11c08c20, FUTEX_WAKE_PRIVATE, 1 <unfinished ...> [pid 18032] write(1, "Thread 2 deleted the object at i"..., 46) = 46 [pid 18031] <... futex resumed> ) = 0 [pid 18032] nanosleep({tv_sec=0, tv_nsec=512000000}, <unfinished ...> [pid 18031] write(1, "Thread 1 created an object at it"..., 45) = 45 [pid 18031] write(1, "Current Time: Tue Jan 14 23:08:3"..., 39) = 39 [pid 18031] write(1, "Thread 1 forgot to delete the ob"..., 55) = 55 [pid 18031] nanosleep({tv_sec=0, tv_nsec=512000000}, <unfinished ...> [pid 18032] <... nanosleep resumed> 0x7fcf10c7ace0) = 0 [pid 18032] stat("/etc/localtime", <unfinished ...> [pid 18031] <... nanosleep resumed> 0x7fcf1147bce0) = 0 [pid 18032] <... stat resumed> {st_mode=S_IFREG|0644, st_size=312, ...}) = 0 [pid 18031] futex(0x7fcf11c08c20, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...> [pid 18032] futex(0x7fcf11c08c20, FUTEX_WAKE_PRIVATE, 1 <unfinished ...> [pid 18031] <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable) [pid 18032] <... futex resumed> ) = 0 [pid 18031] stat("/etc/localtime", <unfinished ...> [pid 18032] write(1, "Thread 2 created an object at it"..., 45 <unfinished ...> [pid 18031] <... stat resumed> {st_mode=S_IFREG|0644, st_size=312, ...}) = 0 [pid 18032] <... write resumed> ) = 45 [pid 18032] write(1, "Current Time: Tue Jan 14 23:08:3"..., 39 <unfinished ...> [pid 18031] futex(0x7fcf11c08c20, FUTEX_WAKE_PRIVATE, 1 <unfinished ...> [pid 18032] <... write resumed> ) = 39 [pid 18031] <... futex resumed> ) = 0 [pid 18032] write(1, "Thread 2 forgot to delete the ob"..., 55 <unfinished ...> [pid 18031] futex(0x7fcf11c088c0, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...> [pid 18032] <... write resumed> ) = 55 [pid 18032] futex(0x7fcf11c088c0, FUTEX_WAKE_PRIVATE, 1) = 1 [pid 18031] <... futex resumed> ) = 0 [pid 18032] nanosleep({tv_sec=0, tv_nsec=512000000}, <unfinished ...> [pid 18031] futex(0x7fcf11c088c0, FUTEX_WAKE_PRIVATE, 1) = 0 [pid 18031] write(1, "Thread 1 created an object at it"..., 45) = 45 [pid 18031] write(1, "Current Time: Tue Jan 14 23:08:3"..., 39) = 39 [pid 18031] write(1, "Thread 1 deleted the object at i"..., 46) = 46 [pid 18031] nanosleep({tv_sec=0, tv_nsec=512000000},
原因分析
- 内存分配器的用户空间缓存:GCC默认使用的glibc内存分配器(ptmalloc)会在用户空间维护内存池。每次
new/delete调用时,分配器优先从内存池中获取或释放内存,只有当内存池不足需要扩容,或者释放的内存足够大需要归还给内核时,才会触发brk、mmap、munmap这类系统调用。你的程序中每个SharedObject对象很小,内存池完全可以满足多次分配需求,因此不会频繁产生系统调用。 - strace跟踪的是系统调用而非库函数:
malloc和free是用户空间的库函数,并非系统调用。strace的核心作用是跟踪进程与内核之间的系统调用交互,所以默认不会显示malloc/free的调用记录。你看到的write、futex、nanosleep都是直接和内核交互的系统调用。 - 编译优化可能加剧该现象:如果编译时开启了优化选项(如
-O2),编译器会对内存分配逻辑做进一步优化,比如合并小分配请求、复用内存块,进一步减少系统调用的触发次数。
解决办法
- 使用
ltrace跟踪库函数:ltrace专门用于跟踪用户空间库函数的调用,能直接看到malloc和free的执行情况,命令如下:ltrace -e malloc,free ./your_program_name - 禁用内存分配器缓存:通过设置环境变量调整ptmalloc的行为,强制每次分配都向内核申请内存,命令如下:
之后再用strace跟踪,就能看到MALLOC_TRIM_THRESHOLD_=0 ./your_program_namebrk/mmap等系统调用。 - 直接跟踪内存相关系统调用:用strace指定跟踪
brk、mmap、munmap这些与内存分配相关的系统调用,命令如下:strace -e brk,mmap,munmap ./your_program_name
内容的提问来源于stack exchange,提问作者Soumajit
相关产品推荐
相关产品推荐

