Как проверить размер кучи для процесса в Linux

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

PS: довольно похожий вопрос, но ответ мне непонятен.

Ответ 1

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

Вы должны проверить текущие лимиты своих систем с помощью команды 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 байт, что примерно соответствует объединенному ram и swap, дисконтированию, используемому другое программное обеспечение и ядро.

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

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

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

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

Ответ 2

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

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

Записывая 2 в /proc/sys/vm/overcommit_memory, вы можете отключить overcommit. Если вы это сделаете, ядро ​​будет проверять, есть ли у него память, прежде чем обещать. Это приведет к тому, что 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.

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

Ответ 3

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

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

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

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

Ответ 4

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

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

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

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

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