Почему личный размер вашего процесса может быть намного больше, чем количество выделенных ресурсов? - PullRequest
2 голосов
/ 06 февраля 2011

Я пытаюсь понять использование памяти программой, которую я поддерживаю.Я понял, что из дампа процесса я могу использовать windbg для выполнения «heap -a 0», чтобы напечатать каждое выделение, которое мой процесс еще не удалил.Каждое распределение выглядит примерно так:

0000000006a905c0: 000a0.000a0 [07] - занято (68), заполнение хвоста - невозможно прочитать дополнительную запись кучи в 0000000006a90650

Где 68 - это то, что я вызвала, а 000a0 - это то, сколько памяти фактически используется (так как есть некоторые накладные расходы).

Если я добавлю все эти выделения для моей программы, я получу 34Mb, однако, это, похоже, не имеет большого отношения к сумме памяти в! Address -summary, которая показывает:

0:000> !address -summary

--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free                                    132      7ff`e905b000 (   8.000 Tb)          100.00%
<unclassified>                         1234        0`11025000 ( 272.145 Mb)  74.04%    0.00%
Image                                   430        0`03fe6000 (  63.898 Mb)  17.38%    0.00%
Stack                                    93        0`01f00000 (  31.000 Mb)   8.43%    0.00%
TEB                                      31        0`0003e000 ( 248.000 kb)   0.07%    0.00%
NlsTables                                 1        0`00024000 ( 144.000 kb)   0.04%    0.00%
ActivationContextData                    22        0`0001e000 ( 120.000 kb)   0.03%    0.00%
CsrSharedMemory                           1        0`00009000 (  36.000 kb)   0.01%    0.00%
PEB                                       1        0`00001000 (   4.000 kb)   0.00%    0.00%

--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_PRIVATE                            1340        0`1167d000 ( 278.488 Mb)  75.76%    0.00%
MEM_IMAGE                               430        0`03fe6000 (  63.898 Mb)  17.38%    0.00%
MEM_MAPPED                               43        0`01932000 (  25.195 Mb)   6.85%    0.00%

--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE                                132      7ff`e905b000 (   8.000 Tb)          100.00%
MEM_RESERVE                             631        0`0e970000 ( 233.438 Mb)  63.51%    0.00%
MEM_COMMIT                             1182        0`08625000 ( 134.145 Mb)  36.49%    0.00%

--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
PAGE_READWRITE                          788        0`04428000 (  68.156 Mb)  18.54%    0.00%
PAGE_EXECUTE_READ                        85        0`027b8000 (  39.719 Mb)  10.81%    0.00%
PAGE_READONLY                           225        0`01984000 (  25.516 Mb)   6.94%    0.00%
PAGE_WRITECOPY                           51        0`00081000 ( 516.000 kb)   0.14%    0.00%
PAGE_READWRITE|PAGE_GUARD                31        0`0003e000 ( 248.000 kb)   0.07%    0.00%
PAGE_EXECUTE_READWRITE                    2        0`00002000 (   8.000 kb)   0.00%    0.00%

--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
Free                                      1`80014000      7fd`cb33c000 (   7.991 Tb)
<unclassified>                            0`0eae9000        0`037d7000 (  55.840 Mb)
Image                                   7ff`7f56e000        0`0062e000 (   6.180 Mb)
Stack                                     0`04120000        0`000fc000 (1008.000 kb)
TEB                                     7ff`fff7c000        0`00002000 (   8.000 kb)
NlsTables                               7ff`fffb0000        0`00024000 ( 144.000 kb)
ActivationContextData                     0`00130000        0`00005000 (  20.000 kb)
CsrSharedMemory                           0`7efe0000        0`00009000 (  36.000 kb)
PEB                                     7ff`fffdf000        0`00001000 (   4.000 kb)

Эти цифры немного сбивают с толку, потому что похоже, что только память, которая и MEM_PRIVATE, и MEM_COMMIT действительно была выделена моей программой ... Остальное в общих DLL и других областях.Это побудило меня попытаться подсчитать все сегменты MEM_PRIVATE, MEM_COMMIT, выполнив:

! Address -f: VAR, MEM_PRIVATE, MEM_COMMIT

Это дает вывод для каждого сегмента, а не каждого malloc.Вот пример:

   0`12bf0000        0`12bf1000        0`00001000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     <unclassified> 
   0`12cf0000        0`12cf5000        0`00005000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     <unclassified> 
   0`12e70000        0`12e75000        0`00005000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     <unclassified> 
   0`37f20000        0`37f21000        0`00001000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unclassified> 
   0`7ffe0000        0`7ffe1000        0`00001000 MEM_PRIVATE MEM_COMMIT  PAGE_READONLY                      <unclassified>

В третьем столбце указан размер сегмента, и если я добавлю все это для своей программы, я получу в общей сложности 68 МБ (примерно вдвое больше, чем общая сумма всех выделений).Так как же мне рационализировать эти две цифры?Что определяет размер этих сегментов и почему итоговая сумма так сильно отличается от всех моих распределений?Кроме того, размер всех моих распределений довольно мал по сравнению с тем, что появляется в диспетчере задач win7 (частный рабочий набор) .... Итак, я где-то пропускаю использование памяти?

Ответы [ 3 ]

1 голос
/ 06 февраля 2011

Я думаю, что теперь я воспроизвел свою проблему с помощью небольшой программы, которая ясно показывает, что происходит. Я подозреваю, что мой синтаксический анализатор (который использует много временных объектов STL, поддерживаемых кучей) выделяет большие объекты в то же время, как я делаю выделения для гораздо более долгосрочных объектов:

Вот крайний пример того, что может произойти, если вы сделаете это с комментариями, показывающими, что отражает диспетчер задач:

   int _tmain(int argc, _TCHAR* argv[])
   {
    void * large_allocs[1024*64];
    void * small_allocs[1024*64];

    prompt("About to allocate memory");
    for (int i = 0; i < _countof(large_allocs); i++)
    {
        AllocateNBytes(&large_allocs[i], 10 * 1024);  
        AllocateNBytes(&small_allocs[i], 1);
    }

        prompt("\Large and small allocations performed");

       //NOTE: Task manager shows 650Mb usage at this point

    for (int i = 0; i < _countof(large_allocs); i++)
        free(large_allocs[i]);
    prompt("Large allocations cleared down, We now only have 64k allocated, but how big is our committed memory?");

        //NOTE: Task manager shows windows taking ages to deal with the fragmented memory, it eventually stops with the memory around 226Mb
        //              for committed and private working set despite now only having 64k of real allocations...  It doesn’t seem to be able to clear down any
        //              further than this...


    for (int i = 0; i < _countof(small_allocs); i++)
        free(small_allocs[i]);
    prompt("Small allocations now cleared down, Notice that the private working set and committed memory take ages to get back down to 0");

        //NOTE: The private working set and committed fall back down to what the program started at but take much longer than if I’d only allocated the 
        //              small allocations in the first place.

    return 0;
    }

    void prompt(char * message)
    {
        printf ("%s\n", message);
        char buf[2];
        printf ("Press any key...\n");
        fgets (buf, 2, stdin);
    }

    void AllocateNBytes( void ** ptr, DWORD sizeptr ) 
    {
        *ptr = malloc ( sizeptr * sizeof(BYTE));
        for (DWORD i = 0; i < sizeptr; i++)
        {
            ((BYTE *) *ptr)[i] = 0;   //Fool the optimiser into thinking we read this
            if (((BYTE *) *ptr)[i] != 0)
            {
                printf("%d", ((BYTE *) *ptr)[i]);
            }
        }
    }

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

1 голос
/ 06 февраля 2011

Добавление вашего распределения не даст вам общую память «частной памяти», используемую кучей.

Для начала выделение виртуальной памяти выполняется с гранулярностью 64 КБ, в куче могут использоваться блоки еще большего размера. Также частная память включает в себя всю память, которая не используется совместно с другими процессами. Это включает в себя память, выделенную VirtualAlloc, а также страницы копирования при записи.

0 голосов
/ 06 февраля 2011

Нет, это не правильный путь. Есть причина, по которой куча хранит эту память, и потому, что ее перераспределение на намного быстрее. Конечно, ОС также выделяет любую память, которую она хочет выделить. Диспетчер памяти решает сохранить его, и делает это потому, что повышает производительность.

Если ваша программа не использует больше памяти, чем может вместить ваша система, вам не следует обращать внимание на использование памяти, главным образом потому, что ОЗУ существует для использования, и, во-вторых, потому что есть шесть десятков миллионов деталей реализации, которые приводят к использованию дополнительной памяти что вы не поймете, и удаление их не принесет вам пользы. Поскольку вы используете Windows, минимальные требования означают, что вы в значительной степени должны работать в системе, которая может достаточно легко оторвать 64 МБ памяти.

Вы можете использовать GlobalMemoryStatusEx , чтобы определить, сколько виртуальной памяти вы используете. Диспетчер задач определенно не является надежным источником информации о профилировании памяти.

...