在Windows中获得准确的堆使用情况(用于在单个进程中调试内存泄漏)

我作为嵌入式软件开发人员已经工作了多年,在该应用程序中,内存泄漏(甚至最小量的内存泄漏)通常非常关键。

在这些环境中,通常可以评估已使用堆的数量,而仅通过在代码关键点上打印已使用的内存总量,就至少可以进行基本/粗糙的内存泄漏调试。

现在,我正在Windows环境中开发C ++解析器,但是...令人惊讶的是,我找不到跟踪此基本信息的方法。所以问题是:我该怎么办?

在回答之前,让我先说一下,出于某些原因我对类似Valgrind的工具不感兴趣

在问一个新问题之前,我已经阅读了很多以前的问题,例如:

How to get memory usage under Windows in C++

Which member in PROCESS_MEMORY_COUNTERS structure gives the current used memory

How to determine CPU and memory consumption from inside a process?

但是他们都没有提供适合我需要的解决方案。因此,我决定写一个新的问题,明确(1)我真正需要什么,(2)我已经尝试实现目标的尝试。因此,下面提供一个最小的示例程序,在该程序中,我将执行一些128kB(0x20000字节)的分配(以不同的方式),然后执行相应的内存释放。每一步之后,我调用一个debugMemory()实用程序,该实用程序将打印PROCESS_MEMORY_COUNTERS_EX结构的每个字段:

#include <stdio.h>
#include <windows.h>
#include <psapi.h>

#define ONE_K 1024

static void debugMemory( const char * header )
{
  PROCESS_MEMORY_COUNTERS_EX pmc;

  if( header )
  {
    printf("%s:\t\tGetProcessMemoryInfo() returned %d\n",header,GetProcessMemoryInfo(getcurrentProcess(),(PROCESS_MEMORY_COUNTERS*)&pmc,sizeof(pmc)));

    printf("%s:\tPageFaultCount\t\t\t= %d 0x%08X\n",pmc.PageFaultCount,pmc.PageFaultCount);
    printf("%s:\tPeakWorkingSetSize\t\t= %d 0x%08X\n",pmc.PeakWorkingSetSize,pmc.PeakWorkingSetSize);
    printf("%s:\tWorkingSetSize\t\t\t= %d 0x%08X\n",pmc.WorkingSetSize,pmc.WorkingSetSize);
    printf("%s:\tQuotaPeakPagedPoolUsage\t\t= %d 0x%08X\n",pmc.QuotaPeakPagedPoolUsage,pmc.QuotaPeakPagedPoolUsage);
    printf("%s:\tQuotaPagedPoolUsage\t\t= %d 0x%08X\n",pmc.QuotaPagedPoolUsage,pmc.QuotaPagedPoolUsage);
    printf("%s:\tQuotaPeakNonPagedPoolUsage\t= %d 0x%08X\n",pmc.QuotaPeakNonPagedPoolUsage,pmc.QuotaPagedPoolUsage);
    printf("%s:\tQuotaNonPagedPoolUsage\t\t= %d 0x%08X\n",pmc.QuotaNonPagedPoolUsage,pmc.QuotaNonPagedPoolUsage);
    printf("%s:\tPagefileUsage\t\t\t= %d 0x%08X\n",pmc.PagefileUsage,pmc.PagefileUsage);
    printf("%s:\tPeakPagefileUsage\t\t= %d 0x%08X\n",pmc.PeakPagefileUsage,pmc.PeakPagefileUsage);

    printf( "%s:\tPrivateUsage\t\t\t= %d 0x%08X\n",pmc.PrivateUsage,pmc.PrivateUsage );
  }
}

int main(void)
{
  /* Initial */
  debugMemory("INI");
  Sleep(5000);

  /* Malloc */
  char *p1 = (char *) malloc(128 * ONE_K);
  debugMemory("MALLOC");
  Sleep(5000);

  /* New */
  char *p2 = new char[128 * ONE_K];
  debugMemory("NEW");
  Sleep(5000);

  /* Free */
  free( p1 );
  debugMemory("FREE");
  Sleep(5000);

  /* Delete */
  delete[] p2;
  debugMemory("DELETE");

  return 0;
}

根据对SO问题的大多数答案,字段WorkingSetSizePrivateUsage是提供我所需信息的最佳人选。无论如何,为了提供一个完整的方案,我将发布所有结果:

INI:            GetProcessMemoryInfo() returned 1
INI:    PageFaultCount                  = 766 0x000002FE
INI:    PeakWorkingSetSize              = 2834432 0x002B4000
INI:    WorkingSetSize                  = 2830336 0x002B3000
INI:    QuotaPeakPagedPoolUsage         = 22448 0x000057B0
INI:    QuotaPagedPoolUsage             = 22448 0x000057B0
INI:    QuotaPeakNonPagedPoolUsage      = 4864 0x000057B0
INI:    QuotaNonPagedPoolUsage          = 4480 0x00001180
INI:    PagefileUsage                   = 1069056 0x00105000
INI:    PeakPagefileUsage               = 1069056 0x00105000
INI:    PrivateUsage                    = 1069056 0x00105000
MALLOC:         GetProcessMemoryInfo() returned 1
MALLOC: PageFaultCount                  = 794 0x0000031A
MALLOC: PeakWorkingSetSize              = 2949120 0x002D0000
MALLOC: WorkingSetSize                  = 2945024 0x002CF000
MALLOC: QuotaPeakPagedPoolUsage         = 22448 0x000057B0
MALLOC: QuotaPagedPoolUsage             = 22448 0x000057B0
MALLOC: QuotaPeakNonPagedPoolUsage      = 4864 0x000057B0
MALLOC: QuotaNonPagedPoolUsage          = 4480 0x00001180
MALLOC: PagefileUsage                   = 1204224 0x00126000
MALLOC: PeakPagefileUsage               = 1204224 0x00126000
MALLOC: PrivateUsage                    = 1204224 0x00126000
NEW:            GetProcessMemoryInfo() returned 1
NEW:    PageFaultCount                  = 797 0x0000031D
NEW:    PeakWorkingSetSize              = 2961408 0x002D3000
NEW:    WorkingSetSize                  = 2957312 0x002D2000
NEW:    QuotaPeakPagedPoolUsage         = 22448 0x000057B0
NEW:    QuotaPagedPoolUsage             = 22448 0x000057B0
NEW:    QuotaPeakNonPagedPoolUsage      = 4864 0x000057B0
NEW:    QuotaNonPagedPoolUsage          = 4480 0x00001180
NEW:    PagefileUsage                   = 1339392 0x00147000
NEW:    PeakPagefileUsage               = 1339392 0x00147000
NEW:    PrivateUsage                    = 1339392 0x00147000
FREE:           GetProcessMemoryInfo() returned 1
FREE:   PageFaultCount                  = 797 0x0000031D
FREE:   PeakWorkingSetSize              = 2961408 0x002D3000
FREE:   WorkingSetSize                  = 2957312 0x002D2000
FREE:   QuotaPeakPagedPoolUsage         = 22448 0x000057B0
FREE:   QuotaPagedPoolUsage             = 22448 0x000057B0
FREE:   QuotaPeakNonPagedPoolUsage      = 4864 0x000057B0
FREE:   QuotaNonPagedPoolUsage          = 4480 0x00001180
FREE:   PagefileUsage                   = 1339392 0x00147000
FREE:   PeakPagefileUsage               = 1339392 0x00147000
FREE:   PrivateUsage                    = 1339392 0x00147000
DELETE:         GetProcessMemoryInfo() returned 1
DELETE: PageFaultCount                  = 797 0x0000031D
DELETE: PeakWorkingSetSize              = 2961408 0x002D3000
DELETE: WorkingSetSize                  = 2957312 0x002D2000
DELETE: QuotaPeakPagedPoolUsage         = 22448 0x000057B0
DELETE: QuotaPagedPoolUsage             = 22448 0x000057B0
DELETE: QuotaPeakNonPagedPoolUsage      = 4864 0x000057B0
DELETE: QuotaNonPagedPoolUsage          = 4480 0x00001180
DELETE: PagefileUsage                   = 1339392 0x00147000
DELETE: PeakPagefileUsage               = 1339392 0x00147000
DELETE: PrivateUsage                    = 1339392 0x00147000

让我们总结一下,从这些结果中我们可以理解:

  • PrivateUsage似乎是我要搜索的字段:每次分配后,它的值都大0x21000(而不是0x20000。但是我可以原谅那些0x1000字节的开销)
  • 它的值 Isn'在内存取消分配(!!!)
  • 后减小
  • 我原本希望在一定时间后将未使用的虚拟内存返还给操作系统(这就是为什么我尝试在每个步骤之后插入5s睡眠的原因),但似乎我错了
  • WorkingSetSize似乎也会在每次分配后增长,但是据我所知,增长的数量并不一致

任何帮助将不胜感激。我对我无法找到的任何 magic 函数和任何变通方法都开放(例如,迫使PrivateUsage所示的已用虚拟内存被更新的东西)。>

yanxiaoyong1234 回答:在Windows中获得准确的堆使用情况(用于在单个进程中调试内存泄漏)

PrivateUsage对于您似乎想做的事情来说是完全可以接受的指标。

得到0x21000而不是0x20000的原因是因为该分配在malloc或new返回的地址之前有一个标头(以便正确释放该分配),内存中已经没有东西可以满足请求,并且底层操作系统调用会为进程获取更多的内存,最终会以页面的倍数形式返回内容,因此必须舍入为页面大小的倍数。

在测试期间PrivateUsage的值不会下降的原因可能是因为正在保存所讨论的缓冲区以满足将来的请求。要验证此猜测,您可以重复执行新的和免费的部分,以便获得“ NEW2”报告和“ FREE2”报告。对于PrivateUsage,我希望“ NEW2”的输出与“ FREE”的输出相同。

本文链接:https://www.f2er.com/3165371.html

大家都在问