Способ определения «реального» использования памяти процессом, то есть частного грязного RSS? - PullRequest
44 голосов
/ 23 сентября 2008

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

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

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

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

Ответы [ 10 ]

41 голосов
/ 23 декабря 2009

На OSX Activity Monitor дает очень хорошие предположения.

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

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

Реальная память - это объем оперативной памяти, в настоящее время «назначенный» вашему процессу, независимо от того, является ли он частным или общим. Это может быть как раз сумма частного и общего, но обычно это не так. Вашему процессу может быть выделено больше памяти, чем ему требуется в настоящее время (это ускоряет запросы на увеличение памяти в будущем), но это не потеря для системы. Если другому процессу требуется память и свободной памяти нет, то до того, как система начнет замену, она заберет эту дополнительную память у вашего процесса и назначит другой процесс (что является быстрой и безболезненной операцией); поэтому ваш следующий вызов 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% памяти было выгружено на диск)

9 голосов
/ 12 октября 2011

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

6 голосов
/ 23 сентября 2008

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

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

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

5 голосов
/ 07 марта 2012

Вы можете получить приватные грязные и приватные чистые RSS из / proc / pid / smaps

5 голосов
/ 29 ноября 2008

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

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

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

3 голосов
/ 10 марта 2014

Взгляни на смем. Это даст вам PSS информацию

http://www.selenic.com/smem/

2 голосов
/ 31 августа 2018

Используйте системный вызов 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>):
2 голосов
/ 17 мая 2013

Переделано это, чтобы быть намного чище, чтобы продемонстрировать некоторые правильные лучшие практики в 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 для сортировки вывода это выглядит следующим образом:

        56: 106496
         1: 147456
        55: 155648
1 голос
/ 15 сентября 2013

Проверьте, это исходный код gnome-system-monitor, он думает, что память " реально использованная " одним процессом является суммой (info->mem) памяти X-сервера (info->memxserver) и ПЗУ (info->memwritable), « ПЗУ » - это блоки памяти, помеченные как « 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);
}
1 голос
/ 07 января 2013

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

Если вы хотите вывод статуса linux style / proc / PROCESSID / status, сделайте следующее:

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

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

...