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

时间:2016-07-20 20:32:06

标签: c++ c linux macos memory

虽然这看起来可能与其他问题重复,但让我解释一下为什么不这样做。

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

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

这个问题(How to determine CPU and memory consumption from inside a process?)提供了查询当前进程使用的虚拟内存量的好方法,我认为这就是我所需要的。

在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字段,我也获得了比预期更大的数字(对于一个在Windows上甚至不接近1 Gb的应用程序,只有几个Gb),但是没有Linux那么大。

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

  • 我更愿意包含应用程序静态数据,代码部分和所有内容,但我可以不用。
  • 我更愿意包括为堆栈分配的内存,但我可以不用。
  • 我更愿意包含共享库使用的内存,但我可以不用。
  • 我不太关心mmap的东西,我可以在那时做或不做。

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

我尝试过的事情

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

  1. 从/ proc / self / status

    读取

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

  2. 阅读/ proc / self / statm

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

  3. 使用mallinfo()uordblks字段

    这似乎并不包括所有分配(我猜测new缺少),因为虚拟内存空间增长了+ 2Gb(在做了一些内存繁重的工作之后仍然抱着记忆),我只看到mallinfo()返回的数字增长了0.1Gb。

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

    此值从大约336,760 Kb开始,最高达到1,019,496 Kb,工作量增加了虚拟内存空间+ 2Gb,然后它永远不会下降,所以我不确定我是否真的可以依赖这个号码......

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

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

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

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

  7. 这就是我所拥有的一切。还有一件事我还没有尝试过,因为它只适用于OSX,但它是使用Why does mstats and malloc_zone_statistics not show recovered memory after free?中描述的方法,即使用malloc_get_all_zones()malloc_zone_statistics(),但我认为这可能与mallinfo()的问题相同,即不考虑所有分配。

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

3 个答案:

答案 0 :(得分:2)

您可以尝试使用getrusage()返回的信息:

#include <sys/time.h>
#include <sys/resource.h>

int getrusage(int who, struct rusage *usage);

struct rusage {
    struct timeval ru_utime; /* user CPU time used */
    struct timeval ru_stime; /* system CPU time used */
    long   ru_maxrss;        /* maximum resident set size */
    long   ru_ixrss;         /* integral shared memory size */
    long   ru_idrss;         /* integral unshared data size */
    long   ru_isrss;         /* integral unshared stack size */
    long   ru_minflt;        /* page reclaims (soft page faults) */
    long   ru_majflt;        /* page faults (hard page faults) */
    long   ru_nswap;         /* swaps */
    long   ru_inblock;       /* block input operations */
    long   ru_oublock;       /* block output operations */
    long   ru_msgsnd;        /* IPC messages sent */
    long   ru_msgrcv;        /* IPC messages received */
    long   ru_nsignals;      /* signals received */
    long   ru_nvcsw;         /* voluntary context switches */
    long   ru_nivcsw;        /* involuntary context switches */
};

如果内存信息不符合您的目的,则观察页面错误计数有助于监控内存压力,这是您要检测的内容。

答案 1 :(得分:2)

您是否尝试过针对Linux的共享库插入程序,以获取上面的第(5)节?只要您的应用程序不是静态链接malloc函数,您就可以在程序和内核malloc之间插入一个新函数。我多次使用这种策略来收集有关内存使用情况的统计信息。

它需要在运行程序之前设置LD_PRELOAD,但不会更改源或二进制文件。在许多情况下,这是一个理想的答案。

以下是malloc内插器的示例:

http://www.drdobbs.com/building-library-interposers-for-fun-and/184404926

你可能也想做calloc和free。调用new通常最终会调用malloc,因此C ++也会被覆盖。

OS X似乎有类似的功能,但我还没有尝试过。

http://tlrobinson.net/blog/2007/12/overriding-library-functions-in-mac-os-x-the-easy-way-dyld_insert_libraries/

- 亚光

答案 2 :(得分: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;
}

由于这是循环遍历/ proc / self / maps中的所有行,因此查询内存的速度明显慢于使用How to determine CPU and memory consumption from inside a process?中“当前进程当前使用的虚拟内存”。

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