【问题标题】:API call to get current heap size of process?获取进程当前堆大小的 API 调用?
【发布时间】:2017-04-26 21:38:06
【问题描述】:

我正在调试大型 C++ 应用程序中的缓慢内存泄漏,我想在程序的各个点打印出 当前 堆大小。

除了打开和解析 /proc/PID/statm 之外,是否有任何库 API 调用可以从中获取这些信息?

一个帖子建议sbrk(),但是,它返回当前堆指针——不是我想要的 100%。 (第二个问题:sbrk()值的变化是否对应当前堆大小的变化?)

我看过,但是没有系统调用似乎很奇怪......

谢谢

更新我

我在调用sbrk() 和读取proc/.../statm 之间做了一些测试比较。 sbrk() 似乎没有反映实际分配。相反,statm 似乎测量实际分配,而sbrk() 显示总堆大小。

这个总堆大小以大块递增(等于页面大小?)。

下面的测试程序产生了这个输出(sbrk() 报告的堆大小/proc/.../statm 报告的内存使用情况,清楚地显示了差异:

0 ALLOC: HEAP SIZE: 0
MEMORY USAGE: 1308 201 174 2 0 566 0
1 ALLOC: HEAP SIZE: 135168
MEMORY USAGE: 1565 212 184 2 0 823 0
2 ALLOC: HEAP SIZE: 135168
MEMORY USAGE: 1822 216 187 2 0 1080 0
3 ALLOC: HEAP SIZE: 135168
MEMORY USAGE: 2079 217 187 2 0 1337 0
4 ALLOC: HEAP SIZE: 135168
MEMORY USAGE: 2336 218 187 2 0 1594 0
5 ALLOC: HEAP SIZE: 135168
MEMORY USAGE: 2593 219 187 2 0 1851 0

0 FREE: HEAP SIZE: 135168
MEMORY USAGE: 3364 225 189 2 0 2622 0
1 FREE: HEAP SIZE: 135168
MEMORY USAGE: 3107 224 189 2 0 2365 0
2 FREE: HEAP SIZE: 135168
MEMORY USAGE: 2850 223 189 2 0 2108 0
3 FREE: HEAP SIZE: 135168
MEMORY USAGE: 2593 222 189 2 0 1851 0
4 FREE: HEAP SIZE: 135168
MEMORY USAGE: 2336 221 189 2 0 1594 0
5 FREE: HEAP SIZE: 135168
MEMORY USAGE: 2079 220 189 2 0 1337 0

测试程序

class CfgProfileList
{
public:
    bool obtainSystemProfileList();
    void leakObjTest();
    std::set<std::string> mProfileList;
private:
    char dummy[1024 * 1024]; // use up some space
};

class ComUtil
{
public:
    static void printMemoryUsage();
private:
    static unsigned int mHeapOrigin;
};

/* static */
unsigned int ComUtil::mHeapOrigin = 0;

// Print current process memory utilization
/* static */ void
ComUtil::printMemoryUsage()
{
    unsigned int pHeap = (unsigned int)sbrk(0);
    if (mHeapOrigin == 0)
        mHeapOrigin = pHeap;

    printf("HEAP SIZE: %u\n", pHeap - mHeapOrigin);

    char fname[256], line[256];
    sprintf(fname, "/proc/%d/statm", getpid());

    FILE *pFile = fopen(fname, "r");
    if (!pFile)
        return;    
    fgets(line, 255, pFile);
    fclose(pFile);
    printf("MEMORY USAGE: %s", line);
}   

void
CfgProfileList::leakObjTest()
{
    CfgProfileList *pointerList[50];
    int  n = 10;
    int  sleep = 100000;

    printf("OBJECT ALLOCATION\n");    
    for (int i = 0; i < n; i++)
    {
        pointerList[i] = new CfgProfileList;
        printf("%d ALLOC: ", i);
        ComUtil::printMemoryUsage();
        usleep(sleep);
    }

    printf("\n");

    for (int i = 0; i < n; i++)
    {
        delete pointerList[i];
        printf("%d FREE: ", i);
        ComUtil::printMemoryUsage();
        usleep(sleep);
    }
}

int
main(int argc, char **argv)
{
    CfgProfileList pl;
    pl.leakObjTest();
}

【问题讨论】:

  • 你考虑过使用 valgrind 或类似的吗?
  • 不幸的是,Valgrind 不起作用;检测到非法指令字节而死。我已经直接询问了 valgrind 开发人员列表,并且根据他们的建议,我已经 a) 从源代码重建 valgrind,b) 将 gcc 重新编译到目标机器的本机处理器。不工作。可以在这里查看相关问题:stackoverflow.com/questions/41088970/…
  • 你的问题是什么?你有非法指令?也许函数指针无效?请添加什么 valgrind 打印。好像是 XY 问题
  • 您可以使用自定义分配器(如果您不担心速度,您可能需要编写一个简单的分配器)来检查内存泄漏。有些地方你也可以使用placement new 操作符(从已经分配的内存中分配内存)
  • Stargateur,您可以在此处查看 valgrind 列表中的完整主题:sourceforge.net/p/valgrind/mailman/valgrind-users/thread/…我已经为这个项目放弃了 valgrind。我在 gperftools 上取得了一些成功!

标签: c++ c linux heap-memory


【解决方案1】:

由于glibc的new是基于malloc(),所以可以使用malloc信息和调试功能;例如,您可以在您的应用程序中添加对malloc_stats() 的调用。

#include <malloc.h>
…
    malloc_stats();

malloc_stats() 函数打印(在标准错误上)有关分配的内存的统计信息 malloc(3) 及相关函数。 …

你也可以看看

  • mallinfo(),

    mallinfo() 函数返回一个结构的副本,其中包含有关由 malloc(3) 执行的内存分配和相关的信息 职能。 …

  • malloc_hook,

    GNU C 库允许您修改 malloc(3)realloc(3) 的行为, 和 free(3) 通过指定适当的钩子函数。您可以使用这些挂钩来帮助您调试使用动态内存分配的程序,例如 例子。

  • mtrace()

    mtrace() 函数为内存分配函数(malloc(3)、realloc(3) memalign(3), 免费(3))。这些钩子函数记录有关内存分配和释放的跟踪信息。追踪信息可用于发现 内存泄漏并试图释放程序中未分配的内存。

【讨论】:

    【解决方案2】:

    我正在调试大型 C++ 应用程序中的缓慢内存泄漏

    您应该尝试使用valgrind(在使用-g 编译所有代码之后)。这是一个非常好的跟踪内存泄漏的工具(但它确实会大大降低您的程序速度,可能至少 x3 或 x10)。

    我想在程序的各个点打印出当前堆大小。

    我不确定 堆大小 是否具有非常明确的含义(但请参阅您问题的其他答案)。操作系统提供并管理您的processvirtual address space,其中的某些部分通常称为“堆”。您可以使用proc(5)parse(在您的程序内部)/proc/self/stat/proc/self/statm/proc/self/status/proc/self/maps,这可以很快。还可以使用pmap(1)ps(1)top(1) 命令(均在内部使用/proc/)。 mmap(2) & munmapmprotect(2)(和旧的sbrk(2)system calls 可以更改您的虚拟地址空间。 C++ new 使用 malloc,而后者经常使用 mmap。但是,freedelete 通常会将已释放的内存区域标记为可供将来malloc-s 重用,但不要将内存放弃给系统。

    PS。如果valgrind 不起作用,我的猜测是你的内存已经损坏,这比内存泄漏更糟糕。考虑尝试一些消毒剂(和其他instrumentation),也许是address sanitizer。当然启用所有警告和调试信息(所以编译withg++ -Wall -Wextra -g)并花时间改进源代码以获得no警告。最近的 GCC 编译器(即 2018 年 8 月的 GCC 8)提供了更好的警告,并且比旧的编译器改进了消毒剂,因此值得更新您的 g++ 编译器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-05-28
      • 1970-01-01
      • 2014-04-26
      • 2012-12-31
      • 2011-11-21
      • 2017-06-03
      • 2021-05-09
      • 2014-10-13
      相关资源
      最近更新 更多