Способ определения процесса "реального" использования памяти, то есть частного грязного RSS?

Инструменты вроде "ps" и "top" сообщают о различных типах использования памяти, таких как размер виртуальной машины и размер резидентного набора. Однако ни одно из них не является "реальным" использованием памяти:

  • Программный код используется совместно несколькими экземплярами одной и той же программы.
  • Программный код общей библиотеки разделяется между всеми процессами, использующими эту библиотеку.
  • Некоторые приложения отключают процессы и совместно используют для них память (например, через сегменты разделяемой памяти).
  • Система виртуальной памяти делает отчет о размерах виртуальной машины практически бесполезным.
  • RSS равен 0, когда процесс выгружается, что делает его не очень полезным.
  • Etc и т.д.

Я обнаружил, что конфиденциальный грязный RSS, как сообщает Linux, является самым близким к "реальному" использованию памяти. Это можно получить, суммируя все значения Private_Dirty в /proc/somepid/smaps.

Однако, другие операционные системы обеспечивают аналогичную функциональность? Если нет, то каковы альтернативы? В частности, меня интересуют FreeBSD и OS X.

Ответ 1

В OSX монитор активности дает вам действительно хорошее предположение.

Частная память - это память, которая используется только вашим приложением. Например. стек памяти и вся память, динамически зарезервированная с использованием malloc() и сопоставимых функций/методов (метод alloc для Objective-C) - это частная память. Если вы используете fork, частная память будет делиться с вами дочерним, но будет отмечена копией на запись. Это означает, что до тех пор, пока страница не будет изменена ни процессом (родителем, ни ребенком), она будет разделяться между ними. Как только любой процесс изменяет любую страницу, эта страница копируется до ее изменения. Даже если эта память разделяется с дочерними элементами fork (и она может только делиться с детьми fork), она по-прежнему отображается как память "private", потому что в худшем случае каждая ее страница будет получать изменен (рано или поздно), а затем снова будет закрыт для каждого процесса.

Общая память - это либо память, которая в настоящее время используется совместно (одни и те же страницы видны в виртуальном пространстве процессов различных процессов), либо, вероятно, станут доступными в будущем (например, постоянная память, поскольку нет оснований для не разделяя память только для чтения). По крайней мере, как я прочитал исходный код некоторых инструментов командной строки от Apple. Поэтому, если вы обмениваетесь памятью между процессами с помощью mmap (или сопоставимого вызова, который отображает одну и ту же память в несколько процессов), это будет разделяемая память. Однако сам исполняемый код также является разделяемой памятью, так как если запускается другой экземпляр вашего приложения, нет причин, по которым он может не использовать уже загруженный в память код (исполняемые кодовые страницы по умолчанию доступны только для чтения, если только вы не используете свой приложение в отладчике). Таким образом, разделяемая память - это действительно память, используемая вашим приложением, так же как и частная, но она может быть дополнительно передана другому процессу (или, возможно, нет, но почему он не учитывает ваше приложение, если оно было общим?)

Реальная память - это объем оперативной памяти, который в настоящее время "назначен" вашему процессу, независимо от того, является ли он приватным или общим. Это может быть точно сумма частных и общих, но обычно это не так. У вашего процесса может быть больше памяти, назначенной ему, чем требуется в настоящее время (это ускоряет запросы на большее количество памяти в будущем), но это не является потерей для системы. Если другой процесс требует памяти и свободной памяти нет, до того, как система начнет замену, она отвлечет эту дополнительную память от вашего процесса и назначит ей другой процесс (который является быстрой и безболезненной операцией); поэтому ваш следующий вызов malloc может быть несколько медленнее. Реальная память также может быть меньше частной и физической памяти; это потому, что если ваш процесс запрашивает память из системы, он получит только "виртуальную память". Эта виртуальная память не связана с какими-либо реальными страницами памяти, если вы ее не используете (поэтому malloc 10 МБ памяти использует только один байт, ваш процесс получит только одну страницу, 4096 байт, выделенной памяти - остальное назначается только в том случае, если вы действительно когда-либо нуждаетесь в этом). Дальнейшая память, которая обменивается, может также не учитываться в реальной памяти (не уверен в этом), но она будет учитываться в общей и частной памяти.

Виртуальная память - это сумма всех блоков адресов, которые считаются действительными в пространстве приложений. Эти адреса могут быть связаны с физической памятью (то есть снова частной или общей), или они могут не быть, но в этом случае они будут связаны с физической памятью, как только вы используете адрес. Доступ к адресам памяти за пределами известных адресов приведет к сбою SIGBUS и вашего приложения. Когда память заменяется, виртуальное адресное пространство для этой памяти остается в силе, и доступ к этим адресам заставляет память обмениваться обратно.

Вывод:
Если ваше приложение явно или неявно использует разделяемую память, частная память - это объем памяти, который требуется вашему приложению из-за размера стека (или размеров при многопоточности) и из-за вызовов malloc(), которые вы сделали для динамической памяти. В этом случае вам не нужно много заботиться о совместной или реальной памяти.

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

Реальная память мало интересна для разработки приложений. Если он больше, чем сумма общих и частных, то это означает ничего, кроме того, что система ленив в памяти за пределами вашего процесса. Если он меньше, тогда ваш процесс запросил больше памяти, чем это было на самом деле, что тоже неплохо, так как, пока вы не используете всю запрошенную память, вы не "крадете" память из системы. Если он намного меньше, чем сумма общих и частных, вы можете рассмотреть возможность запрашивать меньше памяти, если это возможно, поскольку вы немного перегружаете память (опять же, это неплохо, но это говорит мне, что ваш код не является оптимизированный для минимального использования памяти, и если он является кросс-платформой, на других платформах может не быть такой сложной обработки памяти, поэтому вы можете предпочесть выделить много маленьких блоков, а не несколько больших, например, или освободить память намного раньше, и поэтому далее).

Если вы все еще не довольны всей этой информацией, вы можете получить еще больше информации. Откройте терминал и запустите:

sudo vmmap <pid>

где находится идентификатор процесса вашего процесса. Это покажет вам статистику для блока КАЖДЫЙ в вашем пространстве процесса с начальным и конечным адресом. Он также расскажет вам, откуда взялась эта память (сопоставленный файл "Память стека": память Malloc'а - раздел __DATA или __TEXT исполняемого файла?), Насколько он велико в КБ, правах доступа и является ли он конфиденциальным, общий или копирование на запись. Если он отображается из файла, он даже даст вам путь к файлу.

Если вы хотите использовать только "фактическое" использование ОЗУ, используйте

sudo vmmap -resident <pid>

Теперь он покажет для каждого блока памяти, насколько велик блок памяти и насколько он действительно присутствует в физической памяти.

В конце каждого дампа также представлена ​​обзорная таблица с суммами разных типов памяти. Эта таблица выглядит так для Firefox прямо сейчас в моей системе:

REGION TYPE             [ VIRTUAL/RESIDENT]
===========             [ =======/========]
ATS (font support)      [   33.8M/   2496K]
CG backing stores       [   5588K/   5460K]
CG image                [     20K/     20K]
CG raster data          [    576K/    576K]
CG shared images        [   2572K/   2404K]
Carbon                  [   1516K/   1516K]
CoreGraphics            [      8K/      8K]
IOKit                   [  256.0M/      0K]
MALLOC                  [  256.9M/  247.2M]
Memory tag=240          [      4K/      4K]
Memory tag=242          [     12K/     12K]
Memory tag=243          [      8K/      8K]
Memory tag=249          [    156K/     76K]
STACK GUARD             [  101.2M/   9908K]
Stack                   [   14.0M/    248K]
VM_ALLOCATE             [   25.9M/   25.6M]
__DATA                  [   6752K/   3808K]
__DATA/__OBJC           [     28K/     28K]
__IMAGE                 [   1240K/    112K]
__IMPORT                [    104K/    104K]
__LINKEDIT              [   30.7M/   3184K]
__OBJC                  [   1388K/   1336K]
__OBJC/__DATA           [     72K/     72K]
__PAGEZERO              [      4K/      0K]
__TEXT                  [  108.6M/   63.5M]
__UNICODE               [    536K/    512K]
mapped file             [  118.8M/   50.8M]
shared memory           [    300K/    276K]
shared pmap             [   6396K/   3120K]

Что это говорит нам? Например. бинарный файл Firefox и все его загружаемые библиотеки содержат 108 МБ данных в своих разделах __TEXT, но в настоящее время только 63 МБ из них в настоящее время находятся в памяти. Поддержка шрифтов (ATS) требует 33 МБ, но только около 2,5 МБ действительно находятся в памяти. Он использует более чем 5 МБ хранилищ CG, CG = Core Graphics, это, скорее всего, содержимое окна, кнопки, изображения и другие данные, которые кэшируются для быстрого рисования. Он запросил 256 МБ через вызовы malloc, и в настоящее время 247 МБ действительно отображаются на страницах памяти. Он имеет пространство на 14 Мбайт, зарезервированное для стеков, но на данный момент используется только пространство стека 248 КБ.

vmmap также имеет хорошее резюме выше таблицы

ReadOnly portion of Libraries: Total=139.3M resident=66.6M(48%) swapped_out_or_unallocated=72.7M(52%)
Writable regions: Total=595.4M written=201.8M(34%) resident=283.1M(48%) swapped_out=0K(0%) unallocated=312.3M(52%)

И это показывает интересный аспект OS X: для чтения только памяти он не играет никакой роли, если он поменяется или просто нераспределен; существует только резидент, а не резидент. Для записываемой памяти это имеет значение (в моем случае 52% всей запрошенной памяти никогда не использовалось и является таким нераспределенным, 0% памяти было выгружено на диск)

Ответ 2

В Linux вам могут потребоваться номера PSS (пропорциональный размер набора) в /proc/self/smaps. Отображением PSS является его RSS, разделенный на число процессов, которые используют это сопоставление.

Ответ 3

Топ знает, как это сделать. Он показывает VIRT, RES и SHR по умолчанию в Debian Linux. VIRT = SWAP + RES. RES = КОД + ДАННЫЕ. SHR - это память, которая может использоваться совместно с другим процессом (разделяемая библиотека или другая память).

Кроме того, "грязная" память - это только память RES, которая была использована и/или не была заменена.

Трудно сказать, но лучший способ понять - это посмотреть на систему, которая не меняет местами. Затем RES-SHR является исключительной памятью процесса. Однако это не очень хороший способ взглянуть на него, потому что вы не знаете, что память в SHR используется другим процессом. Он может представлять собой неписанные страницы общих объектов, которые используются только процессом.

Ответ 4

Вы действительно не можете.

Я имею в виду, разделяемая память между процессами... вы собираетесь считать это или нет. Если вы не считаете это, вы ошибаетесь; сумма использования памяти всех процессов не будет общим использованием памяти. Если вы посчитаете это, вы будете считать это дважды - сумма не будет правильной.

Я, я доволен RSS. И зная, что вы не можете полностью полагаться на это...

Ответ 5

Вы можете получить конфиденциальный и конфиденциальный чистый RSS из/proc/pid/smaps

Ответ 7

Переделано это, чтобы быть намного чище, чтобы продемонстрировать некоторые правильные лучшие практики в bash, и в частности использовать awk вместо bc.

find /proc/ -maxdepth 1 -name '[0-9]*' -print0 | while read -r -d $'\0' pidpath; do
  [ -f "${pidpath}/smaps" ] || continue
  awk '!/^Private_Dirty:/ {next;}
       $3=="kB" {pd += $2 * (1024^1); next}
       $3=="mB" {pd += $2 * (1024^2); next}
       $3=="gB" {pd += $2 * (1024^3); next}
       $3=="tB" {pd += $2 * (1024^4); next}
       $3=="pB" {pd += $2 * (1024^5); next}
       {print "ERROR!!  "$0 >"/dev/stderr"; exit(1)}
       END {printf("%10d: %d\n", '"${pidpath##*/}"', pd)}' "${pidpath}/smaps" || break
done

На удобном небольшом контейнере на моей машине, с | sort -n -k 2 | sort -n -k 2 для сортировки вывода, это выглядит так:

        56: 106496
         1: 147456
        55: 155648

Ответ 8

Используйте системный вызов mincore (2). Цитирую справочную страницу:

DESCRIPTION
     The mincore() system call determines whether each of the pages in the
     region beginning at addr and continuing for len bytes is resident.  The
     status is returned in the vec array, one character per page.  Each
     character is either 0 if the page is not resident, or a combination of
     the following flags (defined in <sys/mman.h>):

Ответ 9

На вопрос, который упоминается Freebsd, удивился, что никто еще не написал об этом:

Если вам нужен вывод linux style/proc/PROCESSID/status, выполните следующие действия:

mount -t linprocfs none /proc
cat /proc/PROCESSID/status

По крайней мере, во FreeBSD 7.0 установка не была выполнена по умолчанию (7.0 - это гораздо более старая версия, но для чего-то такого основного ответ был скрыт в списке рассылки!)

Ответ 10

Проверьте это, это исходный код gnome-system-monitor, он считает, что память, "действительно используемая" одним процессом, представляет собой сумму (info->mem) X Server Memory (info->memxserver) и Writable Memory ( info->memwritable), " Writable Memory" - это блоки памяти, отмеченные как " Private_Dirty" в файле /proc/PID/smaps.

Помимо системы Linux, может быть другой способ в соответствии с кодом gnome-system-monitor.

static void
get_process_memory_writable (ProcInfo *info)
{
    glibtop_proc_map buf;
    glibtop_map_entry *maps;

    maps = glibtop_get_proc_map(&buf, info->pid);

    gulong memwritable = 0;
    const unsigned number = buf.number;

    for (unsigned i = 0; i < number; ++i) {
#ifdef __linux__
        memwritable += maps[i].private_dirty;
#else
        if (maps[i].perm & GLIBTOP_MAP_PERM_WRITE)
            memwritable += maps[i].size;
#endif
    }

    info->memwritable = memwritable;

    g_free(maps);
}

static void
get_process_memory_info (ProcInfo *info)
{
    glibtop_proc_mem procmem;
    WnckResourceUsage xresources;

    wnck_pid_read_resource_usage (gdk_screen_get_display (gdk_screen_get_default ()),
                                  info->pid,
                                  &xresources);

    glibtop_get_proc_mem(&procmem, info->pid);

    info->vmsize    = procmem.vsize;
    info->memres    = procmem.resident;
    info->memshared = procmem.share;

    info->memxserver = xresources.total_bytes_estimate;

    get_process_memory_writable(info);

    // fake the smart memory column if writable is not available
    info->mem = info->memxserver + (info->memwritable ? info->memwritable : info->memres);
}