Требуется объяснение размера резидентного набора / виртуального размера


61

Я обнаружил, что pidstatэто будет хорошим инструментом для мониторинга процессов. Я хочу рассчитать среднее использование памяти для определенного процесса. Вот пример выходных данных:

02:34:36 PM       PID  minflt/s  majflt/s     VSZ    RSS   %MEM  Command
02:34:37 PM      7276      2.00      0.00  349212 210176   7.14  scalpel

(Это часть вывода из pidstat -r -p 7276.)

Должен ли я использовать информацию о резидентном наборе (RSS) или виртуальном размере (VSZ) для расчета среднего потребления памяти? Я прочитал кое-что в Википедии и на форумах, но я не уверен, что полностью понимаю различия. Кроме того, кажется, что ни один из них не является надежным. Итак, как я могу контролировать процесс, чтобы получить его использование памяти?

Любая помощь по этому вопросу будет полезна.



Ответы:


63

RSS - это количество памяти, которое этот процесс в настоящее время имеет в основной памяти (RAM). VSZ - это количество виртуальной памяти, которое имеет процесс в целом. Это включает в себя все типы памяти, как в оперативной памяти, так и подкачки. Эти числа могут быть искажены, потому что они также включают в себя общие библиотеки и другие типы памяти. У вас может быть пятьсот экземпляров bash, и общий размер их памяти не будет суммой их значений RSS или VSZ.

Если вам необходимо получить более подробное представление об объеме памяти процесса, у вас есть несколько вариантов. Вы можете пройти /proc/$PID/mapи отсеять то, что вам не нравится. Если это общие библиотеки, расчет может быть сложным в зависимости от ваших потребностей (что, я думаю, я помню).

Если вас волнует только размер кучи процесса, вы всегда можете просто проанализировать [heap]запись в mapфайле. Размер, выделенный ядром для кучи процесса, может отражать или не отражать точное количество байтов, которые процесс запросил для выделения. Есть мелкие детали, внутренности ядра и оптимизации, которые могут скинуть это. В идеальном мире это будет столько, сколько нужно вашему процессу, округленное до ближайшего кратного размера системной страницы ( getconf PAGESIZEскажет вам, что это такое - на ПК это, вероятно, 4096 байт).

Если вы хотите увидеть, сколько памяти выделил процесс , одним из лучших способов является отказ от метрик на стороне ядра. Вместо этого вы используете LD_PRELOADмеханизм выделения (удаления) памяти кучи библиотеки C с помощью этого механизма. Лично я немного ругаюсь, valgrindчтобы получить информацию об этом. (Обратите внимание, что применение инструментов потребует перезапуска процесса.)

Обратите внимание: поскольку вы также можете тестировать время выполнения, это valgrindсделает ваши программы немного медленнее (но, вероятно, в пределах ваших допусков).


Большое спасибо! Я собираюсь исследовать различные варианты. Вы были более чем полезны! :)
Flanfl

«Вы можете запустить пятьсот экземпляров bash, и общий размер их памяти не будет суммой их значений RSS или VSZ». Но будет ли сумма их значений RSS хорошим приближением? Как и сумма резидентного столбца из statm, мне не нужно сверхнадежное точное значение, но мне нужно знать на высоком уровне, сколько памяти используют мои процессы Java
iloveretards

3
На Ubuntu это /proc/$PID/mapsопечатка или разница в дистрибутивах?
dolzenko

1

Минимальный исполняемый пример

Чтобы это имело смысл, вы должны понимать основы подкачки: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work и, в частности, что ОС может выделять виртуальную память через таблицы страниц / ведение внутренней памяти (виртуальная память VSZ) до того, как у нее фактически будет резервное хранилище в ОЗУ или на диске (резидентная память RSS).

Теперь, чтобы увидеть это в действии, давайте создадим программу, которая:

  • выделяет больше оперативной памяти, чем наша физическая память с mmap
  • записывает один байт на каждую страницу, чтобы гарантировать, что каждая из этих страниц идет только из виртуальной памяти RSS и VSZ
  • проверяет использование памяти процессом одним из методов, упомянутых по адресу: https://stackoverflow.com/questions/1558402/memory-usage-of-current-process-in-c

main.c

#define _GNU_SOURCE
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

typedef struct {
    unsigned long size,resident,share,text,lib,data,dt;
} ProcStatm;

/* https://stackoverflow.com/questions/1558402/memory-usage-of-current-process-in-c/7212248#7212248 */
void ProcStat_init(ProcStatm *result) {
    const char* statm_path = "/proc/self/statm";
    FILE *f = fopen(statm_path, "r");
    if(!f) {
        perror(statm_path);
        abort();
    }
    if(7 != fscanf(
        f,
        "%lu %lu %lu %lu %lu %lu %lu",
        &(result->size),
        &(result->resident),
        &(result->share),
        &(result->text),
        &(result->lib),
        &(result->data),
        &(result->dt)
    )) {
        perror(statm_path);
        abort();
    }
    fclose(f);
}

int main(int argc, char **argv) {
    ProcStatm proc_statm;
    char *base, *p;
    char system_cmd[1024];
    long page_size;
    size_t i, nbytes, print_interval, bytes_since_last_print;
    int snprintf_return;

    /* Decide how many ints to allocate. */
    if (argc < 2) {
        nbytes = 0x10000;
    } else {
        nbytes = strtoull(argv[1], NULL, 0);
    }
    if (argc < 3) {
        print_interval = 0x1000;
    } else {
        print_interval = strtoull(argv[2], NULL, 0);
    }
    page_size = sysconf(_SC_PAGESIZE);

    /* Allocate the memory. */
    base = mmap(
        NULL,
        nbytes,
        PROT_READ | PROT_WRITE,
        MAP_SHARED | MAP_ANONYMOUS,
        -1,
        0
    );
    if (base == MAP_FAILED) {
        perror("mmap");
        exit(EXIT_FAILURE);
    }

    /* Write to all the allocated pages. */
    i = 0;
    p = base;
    bytes_since_last_print = 0;
    /* Produce the ps command that lists only our VSZ and RSS. */
    snprintf_return = snprintf(
        system_cmd,
        sizeof(system_cmd),
        "ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == \"%ju\") print}'",
        (uintmax_t)getpid()
    );
    assert(snprintf_return >= 0);
    assert((size_t)snprintf_return < sizeof(system_cmd));
    bytes_since_last_print = print_interval;
    do {
        /* Modify a byte in the page. */
        *p = i;
        p += page_size;
        bytes_since_last_print += page_size;
        /* Print process memory usage every print_interval bytes.
         * We count memory using a few techniques from:
         * https://stackoverflow.com/questions/1558402/memory-usage-of-current-process-in-c */
        if (bytes_since_last_print > print_interval) {
            bytes_since_last_print -= print_interval;
            printf("extra_memory_committed %lu KiB\n", (i * page_size) / 1024);
            ProcStat_init(&proc_statm);
            /* Check /proc/self/statm */
            printf(
                "/proc/self/statm size resident %lu %lu KiB\n",
                (proc_statm.size * page_size) / 1024,
                (proc_statm.resident * page_size) / 1024
            );
            /* Check ps. */
            puts(system_cmd);
            system(system_cmd);
            puts("");
        }
        i++;
    } while (p < base + nbytes);

    /* Cleanup. */
    munmap(base, nbytes);
    return EXIT_SUCCESS;
}

GitHub вверх по течению .

Скомпилируйте и запустите:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
echo 1 | sudo tee /proc/sys/vm/overcommit_memory
sudo dmesg -c
./main.out 0x1000000000 0x200000000
echo $?
sudo dmesg

где:

  • 0x1000000000 == 64 ГБ: 2x физической памяти моего компьютера 32 ГБ
  • 0x200000000 == 8 ГБ: печатать память каждые 8 ​​ГБ, поэтому мы должны получить 4 отпечатка до сбоя около 32 ГБ
  • echo 1 | sudo tee /proc/sys/vm/overcommit_memory: требуется для Linux, чтобы мы могли сделать вызов mmap больше физической ОЗУ: https://stackoverflow.com/questions/2798330/maximum-memory-which-malloc-can-allocate/57687432#57687432

Выход программы:

extra_memory_committed 0 KiB
/proc/self/statm size resident 67111332 768 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
  PID    VSZ   RSS
29827 67111332 1648

extra_memory_committed 8388608 KiB
/proc/self/statm size resident 67111332 8390244 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
  PID    VSZ   RSS
29827 67111332 8390256

extra_memory_committed 16777216 KiB
/proc/self/statm size resident 67111332 16778852 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
  PID    VSZ   RSS
29827 67111332 16778864

extra_memory_committed 25165824 KiB
/proc/self/statm size resident 67111332 25167460 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
  PID    VSZ   RSS
29827 67111332 25167472

Killed

Статус выхода:

137

что по правилу 128 + номер сигнала означает, что мы получили номер сигнала 9, который man 7 signalговорит , что это SIGKILL , который посылает убийца нехватки памяти Linux .

Выходная интерпретация:

  • Виртуальная память VSZ остается постоянной printf '0x%X\n' 0x40009A4 KiB ~= 64GiB( psзначения в КиБ) после mmap.
  • RSS «реальное использование памяти» увеличивается лениво только при касании страниц. Например:
    • на первом отпечатке мы имеем extra_memory_committed 0, что означает, что мы еще не коснулись ни одной страницы. RSS - это небольшой файл, 1648 KiBкоторый был выделен для обычного запуска программы, такого как текстовая область, глобальные переменные и т. Д.
    • на втором отпечатке мы написали к 8388608 KiB == 8GiBстоимости страниц. В результате RSS увеличился ровно на 8GIB до8390256 KiB == 8388608 KiB + 1648 KiB
    • RSS продолжает расти с шагом 8 ГБ. Последний отпечаток показывает около 24 ГиБ памяти, и до того, как можно было распечатать 32 ГиБ, убийца OOM убил процесс

См. Также: Требуется объяснение размера резидентного набора / виртуального размера

Журналы убийцы ООМ

Наши dmesgкоманды показали журналы убийцы OOM.

Точная интерпретация этих вопросов была задана по адресу:

Самая первая строка журнала была:

[ 7283.479087] mongod invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0

Итак, мы видим, что интересно, что это был демон MongoDB, который всегда запускался в моем ноутбуке на фоне, который первым вызвал убийцу OOM, предположительно, когда бедняга пытался выделить немного памяти.

Однако убийца ООМ не обязательно убивает того, кто его разбудил.

После вызова ядро ​​печатает таблицу или процессы, включая oom_score:

[ 7283.479292] [  pid  ]   uid  tgid total_vm      rss pgtables_bytes swapents oom_score_adj name
[ 7283.479303] [    496]     0   496    16126        6   172032      484             0 systemd-journal
[ 7283.479306] [    505]     0   505     1309        0    45056       52             0 blkmapd
[ 7283.479309] [    513]     0   513    19757        0    57344       55             0 lvmetad
[ 7283.479312] [    516]     0   516     4681        1    61440      444         -1000 systemd-udevd

и далее мы видим, что наш маленький на main.outсамом деле был убит при предыдущем вызове:

[ 7283.479871] Out of memory: Kill process 15665 (main.out) score 865 or sacrifice child
[ 7283.479879] Killed process 15665 (main.out) total-vm:67111332kB, anon-rss:92kB, file-rss:4kB, shmem-rss:30080832kB
[ 7283.479951] oom_reaper: reaped process 15665 (main.out), now anon-rss:0kB, file-rss:0kB, shmem-rss:30080832kB

В этом журнале упоминается, score 865какой процесс имел, предположительно, самый высокий (наихудший) показатель убийцы OOM, как упомянуто в: Как убийца OOM решает, какой процесс убить первым?

Также интересно то, что все, по-видимому, произошло так быстро, что до того, как освободившаяся память была учтена, процесс oomснова пробудился DeadlineMonitor:

[ 7283.481043] DeadlineMonitor invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0

и на этот раз это убило какой-то процесс Chromium, который обычно является моим компьютером обычным занятием памяти:

[ 7283.481773] Out of memory: Kill process 11786 (chromium-browse) score 306 or sacrifice child
[ 7283.481833] Killed process 11786 (chromium-browse) total-vm:1813576kB, anon-rss:208804kB, file-rss:0kB, shmem-rss:8380kB
[ 7283.497847] oom_reaper: reaped process 11786 (chromium-browse), now anon-rss:0kB, file-rss:0kB, shmem-rss:8044kB

Протестировано в Ubuntu 19.04, ядро ​​Linux 5.0.0.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.