Получение точного использования кучи в Windows (для устранения утечек памяти в одном процессе) - PullRequest
1 голос
/ 04 ноября 2019

Я годами работал разработчиком встроенного ПО, в котором утечки памяти (даже минимальные объемы памяти) часто были действительно критическими.

В этих средах обычно можно было оценить количество использованногокуча, и это сделало возможным, по крайней мере, элементарную / грубую отладку утечки памяти, просто печатая общий объем используемой памяти в ключевых точках кода.

Теперь я разрабатываю синтаксический анализатор C ++ в среде Windows и... удивительно, я не могу найти способ отследить эту основную информацию. Итак, вопрос: как я могу это сделать?

Прежде чем ответить, позвольте мне сказать, что по некоторым причинам Меня не интересуют инструменты, подобные Valgrind .

Дозадавая новый вопрос, я прочитал много предыдущих вопросов, таких как:

Как получить использование памяти под Windows в C ++

Какой член вСтруктура PROCESS_MEMORY_COUNTERS дает текущую используемую память

Как определить потребление ресурсов процессора и памяти внутри процесса?

Но ни один из них не предоставил решения, подходящего для моегонеобходимо. Поэтому я решил написать новый вопрос, в котором проясните (1), что именно мне нужно, и (2) попытку, которую я уже предпринял, пытаясь достичь своей цели. По этой причине ниже я предоставляю минимальный пример программы, в которой я выполняю выделение 128 КБ (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",         header, pmc.PageFaultCount, pmc.PageFaultCount);
    printf("%s:\tPeakWorkingSetSize\t\t= %d 0x%08X\n",       header, pmc.PeakWorkingSetSize, pmc.PeakWorkingSetSize);
    printf("%s:\tWorkingSetSize\t\t\t= %d 0x%08X\n",         header, pmc.WorkingSetSize, pmc.WorkingSetSize);
    printf("%s:\tQuotaPeakPagedPoolUsage\t\t= %d 0x%08X\n",  header, pmc.QuotaPeakPagedPoolUsage, pmc.QuotaPeakPagedPoolUsage);
    printf("%s:\tQuotaPagedPoolUsage\t\t= %d 0x%08X\n",      header, pmc.QuotaPagedPoolUsage, pmc.QuotaPagedPoolUsage);
    printf("%s:\tQuotaPeakNonPagedPoolUsage\t= %d 0x%08X\n", header, pmc.QuotaPeakNonPagedPoolUsage, pmc.QuotaPagedPoolUsage);
    printf("%s:\tQuotaNonPagedPoolUsage\t\t= %d 0x%08X\n",   header, pmc.QuotaNonPagedPoolUsage, pmc.QuotaNonPagedPoolUsage);
    printf("%s:\tPagefileUsage\t\t\t= %d 0x%08X\n",          header, pmc.PagefileUsage, pmc.PagefileUsage);
    printf("%s:\tPeakPagefileUsage\t\t= %d 0x%08X\n",        header, pmc.PeakPagefileUsage, pmc.PeakPagefileUsage);

    printf( "%s:\tPrivateUsage\t\t\t= %d 0x%08X\n", header, 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, поля WorkingSetSize и PrivateUsage были лучшими кандидатамиза предоставление необходимой мне информации. В любом случае, просто для того, чтобы предоставить полный сценарий, я публикую результаты для всех них:

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'T уменьшилось после освобождения памяти (!!!)
  • Я бы ожидал, что неиспользованная виртуальная память вернется в ОС через некоторое время (поэтому я попытался вставить 5sспит после каждого шага) но кажется, что я был не прав
  • WorkingSetSize, кажется, также растет после каждого распределения, но количество грязи непоследовательно, насколько я понимаю

Любая помощь будет очень признательна. Я открыт как для любой функции magic , которую не смог найти, так и для любого обходного трюка (например, что-то, заставляющее использовать виртуальную память, показанную PrivateUsage, для обновления).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...