You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程程序中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的行为,强制每次分配都向内核申请内存,命令如下:
    MALLOC_TRIM_THRESHOLD_=0 ./your_program_name
    
    之后再用strace跟踪,就能看到brk/mmap等系统调用。
  • 直接跟踪内存相关系统调用:用strace指定跟踪brk、mmap、munmap这些与内存分配相关的系统调用,命令如下:
    strace -e brk,mmap,munmap ./your_program_name
    

内容的提问来源于stack exchange,提问作者Soumajit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 00:03:11