Как проверить размер кучи для процесса в Linux - PullRequest
10 голосов
/ 03 декабря 2011

Я писал некоторый код, и он продолжал падать.Позже, после рытья дампов, я понял, что превышаю максимальный предел кучи (жизнь была бы проще, если бы я добавил проверку на malloc).Хотя я это исправил, есть ли способ увеличить размер кучи?

PS: довольно подобный вопрос здесь, но ответ мне неясен.

Ответы [ 4 ]

15 голосов
/ 03 декабря 2011

Управление кучей и памятью - это средство, предоставляемое вашей библиотекой C (скорее всего, glibc).Он поддерживает кучу и возвращает вам куски памяти каждый раз, когда вы делаете malloc().Он не знает предела размера кучи: каждый раз, когда вы запрашиваете больше памяти, чем доступно в куче, он просто отправляет запрос к ядру на большее (используя sbrk() или mmap()).

По умолчанию ядро ​​почти всегда дает вам больше памяти при запросе.Это означает, что malloc() всегда будет возвращать действительный адрес.Только когда вы обращаетесь к выделенной странице в первый раз, ядро ​​действительно потрудится найти страницу для вас.Если он обнаруживает, что не может передать вам один, он запускает OOM killer, который в соответствии с определенной мерой, называемой badness (которая включает в себя размеры виртуальной памяти вашего процесса и его детей, хороший уровень, общее время выполнения и т. Д.), Выбирает жертвуи отправляет его SIGTERM.Этот метод управления памятью называется overcommit и используется ядром, когда /proc/sys/vm/overcommit_memory равен 0 или 1. Подробнее см. overcommit-accounting в документации ядра.

Записав 2 в /proc/sys/vm/overcommit_memory Вы можете отключить сверхкоммит.Если вы сделаете это, ядро ​​на самом деле проверит наличие памяти, прежде чем пообещать.Это приведет к тому, что malloc() вернет NULL, если памяти больше не будет.

Вы также можете установить ограничение на виртуальную память, которую процесс может выделить с помощью setrlimit() и RLIMIT_AS или ulimit -v.команда.Независимо от настройки overcommit, описанной выше, если процесс попытается выделить больше памяти, чем предел, ядро ​​откажется от него, а malloc() вернет NULL.Обратите внимание, что в современном ядре Linux (включая всю серию 2.6.x) ограничение на резидентный размер (setrlimit() с командой RLIMIT_RSS или ulimit -m) неэффективно.

Сеанс ниже был запущен на ядре2.6.32 с 4 ГБ ОЗУ и 8 ГБ подкачки.

$ cat bigmem.c
#include <stdlib.h>
#include <stdio.h>

int main() {
  int i = 0;
  for (; i < 13*1024; i++) {
    void* p = malloc(1024*1024);
    if (p == NULL) {
      fprintf(stderr, "malloc() returned NULL on %dth request\n", i);
      return 1;
    }
  }
  printf("Allocated it all\n");
  return 0;
}
$ cc -o bigmem bigmem.c
$ cat /proc/sys/vm/overcommit_memory
0
$ ./bigmem
Allocated it all
$ sudo bash -c "echo 2 > /proc/sys/vm/overcommit_memory"
$ cat /proc/sys/vm/overcommit_memory
2
$ ./bigmem
malloc() returned NULL on 8519th request
$ sudo bash -c "echo 0 > /proc/sys/vm/overcommit_memory"
$ cat /proc/sys/vm/overcommit_memory
0
$ ./bigmem
Allocated it all
$ ulimit -v $(( 1024*1024 ))
$ ./bigmem
malloc() returned NULL on 1026th request
$

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

Чтобы ответить на ваш вопрос напрямую: если у вас нет предела виртуальной памяти, явно установленного с помощью команды ulimit -v, не существует ограничения размера кучи, кроме физических ресурсов компьютера или логического ограничения вашего адресного пространства (актуально для 32-разрядных систем),Ваш glibc будет продолжать выделять память в куче и будет запрашивать у ядра все больше и больше по мере роста вашей кучи.В конце концов вы можете в конечном итоге поменяться местами, если вся физическая память исчерпана.Как только пространство подкачки исчерпано, случайный процесс будет убит ядром OOM killer.

Обратите внимание, однако, что выделение памяти может произойти сбой по многим причинам, чем из-за недостатка свободной памяти, фрагментации или достижения установленного предела.Вызовы sbrk() и mmap(), используемые распределителем glib, имеют свои собственные сбои, например, разрыв программы достиг другого, уже выделенного адреса (например, совместно используемая память или страница, ранее отображенная с mmap()), или максимальное количество отображений памяти процесса имеетбыл превышен.

9 голосов
/ 03 декабря 2011

Куча обычно равна адресуемой виртуальной памяти в вашей архитектуре.

Вы должны проверить пределы вашей системы с помощью команды ulimit -a и найти эту строку max memory size (kbytes, -m) 3008828, эта строка на моемOpenSuse 11.4 x86_64 с ~ 3,5 ГБ ОЗУ говорит, что у меня примерно 3 ГБ ОЗУ на процесс.

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc,char* argv[]){
        size_t oneHundredMiB=100*1048576;
        size_t maxMemMiB=0;
        void *memPointer = NULL;
        do{
                if(memPointer != NULL){
                        printf("Max Tested Memory = %zi\n",maxMemMiB);
                        memset(memPointer,0,maxMemMiB);
                        free(memPointer);
                }
                maxMemMiB+=oneHundredMiB;
                memPointer=malloc(maxMemMiB);
        }while(memPointer != NULL);
        printf("Max Usable Memory aprox = %zi\n",maxMemMiB-oneHundredMiB);
        return 0;
}

Эта программа получает память с шагом 100 МБ, отображает текущую выделенную память, выделяет 0, затем освобождает память.Когда система не может выделить больше памяти, возвращает NULL и отображает окончательное максимальное полезное количество оперативной памяти.

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

Это, как говорится.В моей системе, где я пишу это, ничего не зависало.И программа выше сообщает почти так же, как ulimit -a.Разница заключается в том, что он фактически протестировал память и с помощью memset() подтвердил, что память была предоставлена ​​и использована.

Для сравнения на виртуальной машине Ubuntu 10.04x86 с 256 МБ ОЗУ и 400 МБ подкачки отчет ulimitбыло memory size (kbytes, -m) unlimited, и моя маленькая программа выдала 524.288.000 байт, что примерно равно объему оперативной памяти и подкачки, дисконтной памяти, используемой другим программным обеспечением и ядром.

Редактировать: Как писал Адам Залкман, ulimit -mбольше не соблюдается в более новых ядрах Linux 2.6 и выше, так что я исправлен.Но ulimit -v это честь.Для практических результатов вы должны заменить -m на -v и искать virtual memory (kbytes, -v) 4515440.Кажется, просто шанс, что мой suse box имеет значение -m, совпадающее с тем, что сообщала моя маленькая утилита.Вы должны помнить, что это виртуальная память, назначаемая ядром, если физической памяти недостаточно, потребуется место подкачки для ее восполнения.

Если вы хотите узнать, сколько физической памяти доступно, не нарушая какой-либо процессили систему, вы можете использовать

long total_available_ram =sysconf(_SC_AVPHYS_PAGES) * sysconf(_SC_PAGESIZE) ;

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

2 голосов
/ 03 декабря 2011

Я думаю, ваша изначальная проблема заключалась в том, что malloc не удалось выделить запрошенную память в вашей системе.

Почему это произошло, зависит от вашей системы.

Когда процесс загружен, ему выделяется память до определенного адреса, который является системной точкой останова для процесса.За этим адресом память не отображается для процесса.Поэтому, когда процесс «достигает» точки «останова», он запрашивает больше памяти у системы, и один из способов сделать это через системный вызов sbrk
malloc сделает это под капотом, нов вашей системе по какой-то причине это не удалось.

Например, для этого может быть много причин:
1) Я думаю, что в Linux существует ограничение на максимальный объем памяти.Я думаю, что это ulimit и, возможно, вы ударили это.Проверьте, не установлен ли лимит
2) Возможно, ваша система была слишком загружена
3) Ваша программа плохо управляет памятью, и у вас в итоге возникает испорченная память, поэтому malloc не может получить запрошенный вами размер чанка.
4) Ваша программа повреждает внутренние структуры данных malloc, то есть неправильное использование указателя
и т. Д.

0 голосов
/ 24 января 2019

Я бы хотел добавить один балл к предыдущим ответам.

Приложения имеют иллюзию, что malloc () возвращает «сплошные» блоки; в действительности буфер может существовать разбросанным, распыленным на многих страницах оперативной памяти. Ключевым фактом здесь является следующее: виртуальная память процесса, содержащая его код или содержащую что-то в виде большого массива, должна быть непрерывной. Давайте даже признаем, что код и данные разделены; большой массив char str [universe_size] должен быть смежным.

Теперь: может ли одно приложение произвольно увеличить кучу, чтобы выделить такой массив?

Ответ может быть «да», если в машине больше ничего не работает. Куча может быть смехотворно огромной, но она должна иметь границы. В какой-то момент вызов sbrk () (в Linux, функция, которая вкратце «увеличивает» кучу) должна наткнуться на область, зарезервированную для другого приложения.

Эта ссылка содержит несколько интересных и поясняющих примеров, зацените. Я не нашел информацию о Linux.

...