如何查询Linux(和OSX)上分配的内存量?

Mar*_*son 11 c c++ linux memory macos

虽然这可能看起来像其他问题的重复,但让我解释为什么它不是.

我希望在达到某个内存限制时,让我的应用程序的特定部分优雅地降级.我可以使用基于剩余可用物理内存的标准,但这不安全,因为操作系统可能会在达到标准之前开始分页我的应用程序使用的内存,这会认为仍有一些物理内存,并保持因为同样的原因,我不能使用进程当前使用的物理内存量,因为一旦操作系统开始交换我,我会继续分配作为操作系统页面的内存,所以数字会不再成长

出于这个原因,我根据应用程序分配的内存量选择了一个标准,即非常接近虚拟内存大小.

这个问题(如何从进程内部确定CPU和内存消耗?)提供了查询当前进程使用的虚拟内存量的好方法,我认为这就是我所需要的.

在Windows上,我正在使用GetProcessMemoryInfo()PrivateUsage现场,这很棒.

在Linux上,我尝试了几个不起作用的东西(如下所列).虚拟内存使用对我不起作用的原因是因为在Linux上的NVidia硬件上创建OpenCL上下文时会发生这种情况.驱动程序保留虚拟内存空间的区域,该区域足以容纳所有RAM,所有交换和所有视频内存.我猜这是统一地址空间和所有内容.但这也意味着该过程报告使用大量内存.例如,在我的系统上,top将报告VIRT列中的23.3 Gb(12 Gb RAM,6 Gb交换,2 Gb视频内存,其中NVidia驱动程序保留20 Gb).

在OSX上,通过使用task_info()virtual_size字段,我也得到一个比预期更大的数字(一个应用程序的几个Gb,在Windows上甚至不接近1 Gb),但没有Linux那么大.

所以这是一个大问题:如何获得我的应用程序分配的内存量?我知道这是一个有点模糊的问题("分配内存"意味着什么?),但我很灵活:

  • 我宁愿包括应用程序静态数据,代码部分和一切,但我可​​以没有.
  • 我宁愿包括为堆栈分配的内存,但我可以没有.
  • 我宁愿包括共享库使用的内存,但我可以没有.
  • 我真的不关心mmap的东西,我可以做或不做.
  • 等等.

真正重要的是数量随着动态分配(new,malloc,any)而增长,并在内存释放时缩小(我知道这可能与实现有关).

我尝试过的事情

以下是我尝试和/或想过的一些解决方案,但这对我不起作用.

  1. 从/ proc/self/status中读取

    这是如何从进程内部确定cpu和内存消耗所建议的方法.但是,如上所述,这将返回虚拟内存量,这对我不起作用.

  2. 从/ proc/self/statm中读取

    非常糟糕:根据http://kernelnewbies.kernelnewbies.narkive.com/iG9xCmwB/proc-pid-statm-doesnt-match-with-status,它指的是Linux内核代码,这两个值之间的唯一区别是第二个不会减去reserved_vm虚拟内存的数量.我希望HOPED reserved_vm包含OpenCL驱动程序保留的内存,但事实并非如此.

  3. 使用mallinfo()uordblks领域

    这似乎并不包括所有的分配(我猜测它们new都缺失了),因为虚拟内存空间增加了2Gb(在做了一些内存繁重的工作并仍然保留了内存之后),我只是看到了返回的数量增长约0.1Gb mallinfo().

  4. 从/ proc/self/smaps中读取[heap]节大小

    这个值从大约336,760 Kb开始,最高达到1,019,496 Kb,因为虚拟内存空间增加了+ 2Gb,然后它永远不会下降,所以我不确定我真的不能依赖这个数字......

  5. 监视我的应用程序中的所有内存分配

    是的,在一个理想的世界里,我可以控制每个分配内存的人.但是,这是一个遗留应用程序,使用大量不同的分配器,一些mallocs,一些news,一些特定于操作系统的例程等.有一些插件可以做任何他们想做的事情,它们可以用不同的编译器编译,因此,虽然这对于真正控制内存很有用,但这在我的上下文中不起作用.

  6. 在OpenCL上下文初始化之前和之后读取虚拟内存大小

    虽然这可能是解决问题的"hacky"方法(我可能不得不回避它),但我真的希望有一种更可靠的查询内存的方法,因为OpenCL上下文可以在我无法控制的地方初始化,并且其他类似但非OpenCL的具体问题可能会蔓延,我不知道.

这就是我所拥有的一切.还有一件事我还没有尝试过,因为它只能在OSX上运行,但它是使用为什么mstats和malloc_zone_statistics在免费后没有显示恢复的内存中描述的方法,即使用malloc_get_all_zones()malloc_zone_statistics(),但我认为这可能是同一个问题mallinfo(),即不考虑所有分配.

因此,任何人都可以提出一种方法来查询Linux中给定进程的内存使用情况(如上所述,对于精确度而言是模糊的),以及OSX,即使它是一种不同的方法也是如此)?

Mar*_*son 0

这是我最终使用的。我扫描 /proc/self/maps 并对符合我的标准的所有地址范围的大小进行求和,即:

  • 仅包含从 inode 0 开始的范围(即没有设备、没有映射文件等)
  • 仅包含至少可读、可写或可执行之一的范围
  • 只包含私有内存
    • 在我的实验中,我没有看到来自 inode 0 的共享内存实例。也许是进程间共享内存......?

这是我的解决方案的代码:

size_t getValue()
{
    FILE* file = fopen("/proc/self/maps", "r");
    if (!file)
    {
        assert(0);
        return 0;
    }

    size_t value = 0;

    char line[1024];
    while (fgets(line, 1024, file) != NULL)
    {
        ptrdiff_t start_address, end_address;
        char perms[4];
        ptrdiff_t offset;
        int dev_major, dev_minor;
        unsigned long int inode;
        const int nb_scanned = sscanf(
            line, "%16tx-%16tx %c%c%c%c %16tx %02x:%02x %lu",
            &start_address, &end_address,
            &perms[0], &perms[1], &perms[2], &perms[3],
            &offset, &dev_major, &dev_minor, &inode
            );
        if (10 != nb_scanned)
        {
            assert(0);
            continue;
        }

        if ((inode == 0) &&
            (perms[0] != '-' || perms[1] != '-' || perms[2] != '-') &&
            (perms[3] == 'p'))
        {
            assert(dev_major == 0);
            assert(dev_minor == 0);
            value += (end_address - start_address);
        }
    }

    fclose(file);

    return value;
}
Run Code Online (Sandbox Code Playgroud)

由于这是循环遍历 /proc/self/maps 中的所有行,因此以这种方式查询内存比使用如何确定进程内部的 CPU 和内存消耗? 中的“当前进程当前使用的虚拟内存”要慢得多。

然而,它提供的答案更接近我所需要的。