无法使用 GetProcessMemoryInfo 测量静态数组内存使用情况

Posted

技术标签:

【中文标题】无法使用 GetProcessMemoryInfo 测量静态数组内存使用情况【英文标题】:Unable to measure static arrays memory usage with GetProcessMemoryInfo 【发布时间】:2017-03-17 08:38:32 【问题描述】:

我正在尝试了解有关内存使用工作的详细信息,以及如何使用 C++ 对其进行测量。我知道在 Windows 下,当包括<Windows.h> 时,一种快速检索当前应用程序进程正在使用的 RAM 量的方法是:

PROCESS_MEMORY_COUNTERS info;
GetProcessMemoryInfo( GetCurrentProcess( ), &info, sizeof(info) );
(uint64_t)info.WorkingSetSize;

然后,我用它来运行一个非常简单的测试:

#include <iostream>
#include <Windows.h>"

int main(void)

    uint64_t currentUsedRAM(0);

    PROCESS_MEMORY_COUNTERS info;
    GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
    currentUsedRAM = info.WorkingSetSize;

    const int N(1000000);
    int x[N]; //in the second run, comment this line out
    int y[N]; //in the second run, comment this line out
    //int *x = new int[N]; //in the second run UNcomment this line out
    //int *y = new int[N]; //in the second run UNcomment this line out
    for (int i = 0; i < N; i++)
    
        x[i] = 1;
        y[i] = 2;
    

    GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
    currentUsedRAM = info.WorkingSetSize - currentUsedRAM;

    std::cout << "Current RAM used: " << currentUsedRAM << "\n";

    return 0;

当我运行上面的代码时,我完全不明白,输出是:Current RAM used: 0,而我期待大约 8mb,因为我填充了两个 1D int 数组,每个数组有 100 万个条目。现在,如果我重新运行代码但使 xy 成为动态分配的数组,那么现在的输出正如预期的那样:Current RAM used: 8007680

这是为什么呢?如何让它在这两种情况下检测内存使用情况?

【问题讨论】:

我很惊讶您的程序甚至启动了,因为您在堆栈上为两个数组分配了近 8 兆的内存。 Windows 上的默认堆栈大小通常是单个 MB。有了这个,你还应该得到一个关于为什么你没有得到预期结果的提示。 @Someprogrammerdude 正是让我产生了疑问。我的直觉恰恰是该函数只测量堆上的内存分配,而不是堆栈上的内存分配。但是,由于应用程序运行,我无法理解发生了什么。因此,只是该函数仅获取堆内存使用量。有没有办法计算堆栈内存使用量? 编译优化了这两个数组。你可以做一些副作用来防止这种情况:尝试输出它们元素的总和。 【参考方案1】:

编译器已优化您的代码。事实上,对于您的第一次运行,既没有分配 x 也没有分配 y。考虑到有明显的副作用:GetProcessMemoryInfo 的返回值,这种优化似乎有点奇怪。

无论如何,您可以通过添加一些其他副作用来防止这种情况,例如输出这两个数组的每个元素的总和,这将保证崩溃。

为具有自动存储持续时间的本地对象分配内存发生在at the beginning of the enclosing code block and deallocated at the end。因此,您的代码无法测量 main 中任何自动存储持续时间变量的内存使用情况(也无法测量我已删除的代码 sn-p,我不知道)。但是对于那些具有动态存储持续时间的对象来说,情况就不同了,它们是根据请求分配的。


我为评论区的讨论设计了一个涉及回避的测试。您可以看到,如果程序更深入,内存使用量会增加。这证明它计算了堆栈上的内存使用量。顺便说一句,这不是计算您的对象需要多少内存,而是您的程序需要多少。

void foo(int depth, int *a, int *b, uint64_t usage) 
  if (depth >= 100)
    return ;
  int x[100], y[100];

  for (int i = 0; i < 100; i++)
  
    x[i] = 1 + (a==nullptr?0:a[i]);
    y[i] = 2 + (b==nullptr?0:b[i]);
  

  PROCESS_MEMORY_COUNTERS info;
  GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));

  std::cout << "Current RAM used: " << info.WorkingSetSize - usage << "\n";

  foo(depth+1,x,y,usage);

  int sum = 0;
  for (int i=0; i<100; i++)
    sum += x[i] + y[i];

  std::cout << sum << std::endl;


int main(void)

  uint64_t currentUsedRAM(0);
  PROCESS_MEMORY_COUNTERS info;
  GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
  currentUsedRAM = info.WorkingSetSize;

  foo(0, nullptr, nullptr, currentUsedRAM);

  return 0;


/*
Current RAM used: 0
Current RAM used: 61440
Current RAM used: 65536
Current RAM used: 65536
Current RAM used: 65536
Current RAM used: 65536
Current RAM used: 69632
Current RAM used: 69632
Current RAM used: 69632
Current RAM used: 69632
Current RAM used: 69632
Current RAM used: 73728
*/

系统每次分配4k,即一页大小。我不知道为什么它会变成 0,然后突然变成 61440。解释 windows 如何管理内存非常困难,而且远远超出了我的能力,尽管我对 4k 的东西很有信心......而且它确实计算了内存使用量对于具有自动存储期限的变量。

【讨论】:

我猜改变 N 不会改变当前使用的 RAM 是因为堆栈的大小是固定的。 我不这么认为。尝试将循环的迭代次数从 1 更改为在堆栈中可以达到的高度(即大约 250000)。您将看到使用的 RAM 量没有改变。您正在捕获的 RAM 与创建的数组无关。数组不计算在内,因为它都在堆栈和函数中,我猜只计算堆内存。由于调用了非常昂贵的std::cout,您获得的 RAM 使用量完全是由于。如果您注释掉其他所有内容并只留下std::cout &lt;&lt; 1 &lt;&lt; std::endl,您将获得相同的 RAM 使用率。 (抱歉,我在尝试编辑时不小心删除了第一条评论。但现在我对我在其中所说的话充满信心,因为正如您在上面的新评论中看到的那样,我已经对其进行了测试。按照我刚才说的,你会看到)

以上是关于无法使用 GetProcessMemoryInfo 测量静态数组内存使用情况的主要内容,如果未能解决你的问题,请参考以下文章

Windows API一日一练 91 GetProcessMemoryInfo函数

尝试在 C++、MinGW 上获取当前进程内存大小时出错

取当前进程内存使用量

MFC下获取系统内存和当前进程的内存使用情况

delphi jinchengneicun

API获取当前进程的内存信息