Я видел несколько сообщений в Интернете, в которых люди, по-видимому, жалуются на то, что VPS-сервер неожиданно убивает процессы, поскольку они используют слишком много оперативной памяти.
Как это возможно? Я думал, что все современные ОС предоставляют «бесконечную оперативную память», просто используя замену диска для всего, что выходит за пределы физической памяти. Это верно?
Что может произойти, если процесс "убит из-за нехватки оперативной памяти"?
Ответы:
Иногда говорят, что linux по умолчанию никогда не отклоняет запросы дополнительной памяти из кода приложения - например
malloc()
. 1 На самом деле это не так; по умолчанию используется эвристика, посредством которойОт
[linux_src]/Documentation/vm/overcommit-accounting
(все цитаты из дерева 3.11). Точно то, что считается «серьезно диким распределением», не указывается явно, поэтому нам нужно было бы просмотреть источник, чтобы определить детали. Мы также могли бы использовать экспериментальный метод в сноске 2 (ниже), чтобы попытаться получить некоторое отражение эвристики - исходя из этого, мое первоначальное эмпирическое наблюдение состоит в том, что при идеальных обстоятельствах (== система бездействует), если вы не Если у вас нет свопа, вам будет позволено выделить около половины вашей оперативной памяти, а если у вас есть своп, вы получите около половины вашей оперативной памяти плюс весь своп. Это более или менее на процесс (но обратите внимание, что этот предел является динамическим и может изменяться в зависимости от состояния, см. Некоторые наблюдения в сноске 5).Половина вашей оперативной памяти плюс подкачка явно является значением по умолчанию для поля «CommitLimit» в
/proc/meminfo
. Вот что это означает - и обратите внимание, что на самом деле это не имеет ничего общего с только что обсужденным пределом (из[src]/Documentation/filesystems/proc.txt
):В ранее цитируемом документе о оверкомит-бухгалтерии указано, что по умолчанию установлено значение
vm.overcommit_ratio
50. Так что если выsysctl vm.overcommit_memory=2
, то можете настроить vm.covercommit_ratio (сsysctl
) и увидеть последствия. 3 Режим по умолчанию, когдаCommitLimit
он не применяется и только «очевидные превышения адресного пространства отклоняются», это когдаvm.overcommit_memory=0
.В то время как стратегия по умолчанию имеет эвристический лимит для каждого процесса, предотвращающий «серьезно дикое распределение», она оставляет систему в целом свободной, чтобы получить серьезно дикое, мудрое распределение. 4 Это означает, что в какой-то момент он может исчерпать память и должен объявить о банкротстве некоторых процессов через убийцу OOM .
Что убивает убийца ООМ? Не обязательно процесс, который запрашивал память, когда его не было, поскольку это не обязательно действительно виновный процесс, и, что более важно, не обязательно тот, который быстрее всего поможет системе справиться с проблемой, в которой она находится.
Это цитируется здесь, который, вероятно, ссылается на источник 2.6.x:
Что кажется приличным обоснованием. Тем не менее, без криминалистической экспертизы, # 5 (который является избыточным # 1) кажется разумной реализацией, а # 3 является избыточным # 2. Так что может иметь смысл рассмотреть это урезанное до # 2/3 и # 4.
Я пролистал недавний источник (3.11) и заметил, что этот комментарий за это время изменился:
Это немного более явно о # 2: «Цель состоит в том, чтобы [убить] задачу, потребляющую наибольшее количество памяти, чтобы избежать последующих сбоев oom», и, как следствие, # 4 ( «мы хотим убить минимальное количество процессов ( один )» ) .
Если вы хотите увидеть убийцу OOM в действии, см. Сноску 5.
1 Бред, от которого Жиль, к счастью, избавился, см. Комментарии.
2 Вот простой фрагмент кода C, который запрашивает все большие и большие куски памяти, чтобы определить, когда запрос на больше не будет работать:
Если вы не знаете, C, вы можете скомпилировать это
gcc virtlimitcheck.c -o virtlimitcheck
, а затем запустить./virtlimitcheck
. Он абсолютно безвреден, так как процесс не использует пространство, которое он запрашивает, т. Е. Он никогда не использует ОЗУ.В системе 3.11 x86_64 с системой 4 ГБ и 6 ГБ подкачки произошел сбой при ~ 7400000 кБ; число колеблется, поэтому, возможно, состояние является фактором. По совпадению это близко к
CommitLimit
in/proc/meminfo
, но изменение через viavm.overcommit_ratio
не имеет никакого значения. Однако на 3.6.11 32-битной системе ARM 448 МБ с 64 МБ подкачки у меня не получается ~ 230 МБ. Это интересно, поскольку в первом случае объем почти вдвое превышает объем ОЗУ, тогда как во втором он составляет около 1/4, что является сильным следствием того, что объем подкачки является фактором. Это было подтверждено отключением свопинга в первой системе, когда порог отказа снизился до ~ 1,95 ГБ, что очень похоже на маленькую коробку ARM.Но так ли это на процесс? Похоже, что Нижеприведенная короткая программа запрашивает определенный пользователем кусок памяти, и, если это удается, ждет, когда вы нажмете return - таким образом, вы можете попробовать несколько одновременных экземпляров:
Однако помните, что речь идет не только об объеме оперативной памяти и обменивании независимо от использования - см. Сноску 5 для наблюдений о влиянии состояния системы.
3
CommitLimit
относится к количеству адресного пространства, разрешенного для системы, когда vm.overcommit_memory = 2. Предположительно, тогда сумма, которую вы можете выделить, должна быть за вычетом того, что уже зафиксировано, что, очевидно, являетсяCommitted_AS
полем.Потенциально интересный эксперимент, демонстрирующий это, заключается в добавлении
#include <unistd.h>
к вершине virtlimitcheck.c (см. Сноску 2) иfork()
непосредственно передwhile()
циклом. Это не гарантирует работу, как описано здесь, без некоторой утомительной синхронизации, но есть немалый шанс, YMMV:Это имеет смысл - детально изучая tmp.txt, вы можете видеть, как процессы чередуют свое все большее и большее распределение (это проще, если вы добавляете pid в вывод), пока один, очевидно, не потребует достаточно, чтобы другой отказал. Тогда победитель может забрать все до
CommitLimit
минусCommitted_AS
.4 Стоит отметить, что на данном этапе, если вы еще не понимаете виртуальную адресацию и разбиение на страницы по требованию, то, что делает возможным в первую очередь перерасход, это то, что ядро выделяет процессам пользовательского пространства вообще не физическую память - это виртуальное адресное пространство . Например, если процесс резервирует 10 МБ для чего-либо, это представляется как последовательность (виртуальных) адресов, но эти адреса еще не соответствуют физической памяти. При обращении к такому адресу это приводит к ошибке страницыа затем ядро пытается отобразить его в реальную память, чтобы оно могло хранить реальное значение. Процессы обычно резервируют гораздо больше виртуального пространства, чем они фактически получают, что позволяет ядру наиболее эффективно использовать оперативную память. Тем не менее, физическая память по-прежнему является ограниченным ресурсом, и когда все это было сопоставлено с виртуальным адресным пространством, некоторое виртуальное адресное пространство должно быть удалено, чтобы освободить часть оперативной памяти.
5 Первое предупреждение : если вы попробуете это с помощью
vm.overcommit_memory=0
, убедитесь, что вы сначала сохранили свою работу и закрыли все критически важные приложения, потому что система зависнет на ~ 90 секунд, а некоторые процессы умрут!Идея состоит в том, чтобы запустить бомбу-вилку, которая истекает через 90 секунд, при этом вилки распределяют пространство, и некоторые из них записывают большие объемы данных в ОЗУ, все время сообщая о них в stderr.
Скомпилируйте это
gcc forkbomb.c -o forkbomb
. Сначала попробуйте это сsysctl vm.overcommit_memory=2
- вы, вероятно, получите что-то вроде:В этой среде подобная вилочная бомба не очень далеко заходит. Обратите внимание, что число в «говорит N вилок» - это не общее количество процессов, а число процессов в цепочке / ветви, ведущих к этому.
Теперь попробуйте это с
vm.overcommit_memory=0
. Если вы перенаправляете stderr в файл, вы можете выполнить грубый анализ, например:Только 15 процессов не удалось выделить 1 ГБ - демонстрации того, что эвристика для overcommit_memory = 0 будет зависит от состояния. Сколько процессов было там? Глядя на конец tmp.txt, вероятно,> 100 000. Теперь, как на самом деле можно использовать 1 ГБ?
Восемь - что опять-таки имеет смысл, поскольку в то время у меня было ~ 3 ГБ свободной оперативной памяти и 6 ГБ подкачки.
Посмотрите ваши системные журналы после того, как вы это сделаете. Вы должны увидеть результаты отчетов убийцы OOM (среди прочего); предположительно это относится к
oom_badness
.источник
Это не случится с вами, если вы загрузите только 1G данных в память. Что делать, если вы загружаете намного больше? Например, я часто работаю с огромными файлами, содержащими миллионы вероятностей, которые необходимо загрузить в R. Это занимает около 16 ГБ ОЗУ.
Запуск вышеупомянутого процесса на моем ноутбуке приведет к тому, что он начнет меняться как сумасшедший, как только мои 8 ГБ ОЗУ будут заполнены. Это, в свою очередь, замедлит все, потому что чтение с диска намного медленнее, чем чтение из ОЗУ. Что если у меня ноутбук с 2 ГБ оперативной памяти и только 10 ГБ свободного места? Как только процесс заберет всю оперативную память, он также заполнит диск, потому что он пишет для подкачки, и у меня больше нет ОЗУ и больше места для подкачки (люди склонны ограничивать подкачку выделенным разделом, а не файл подкачки именно по этой причине). Вот тут и приходит убийца ООМ и начинает убивать.
Таким образом, система действительно может исчерпать память. Кроме того, системы с сильной заменой могут стать непригодными для использования задолго до того, как это произойдет просто из-за медленных операций ввода-вывода из-за замены. Как правило, каждый хочет избежать как можно большего обмена. Даже на высокопроизводительных серверах с быстрыми твердотельными накопителями наблюдается явное снижение производительности. На моем ноутбуке с классическим приводом 7200 об / мин любая значительная замена делает систему непригодной для использования. Чем больше это меняет, тем медленнее становится. Если я не убью процесс оскорбления быстро, все зависнет, пока не вмешается убийца OOM.
источник
Процессы не убиваются, когда ОЗУ больше нет, они убиваются, когда их обманули таким образом:
Это может произойти, даже если система не выполняет активную перестановку, например, если область подкачки заполнена страницами памяти спящих демонов.
Это никогда не происходит в ОС, которые не перегружают память. С ними случайный процесс не убивается, но первый процесс, запрашивающий виртуальную память, пока она исчерпана, имеет malloc (или аналогичный), возвращающийся с ошибкой. Таким образом, это дает возможность правильно справиться с ситуацией. Тем не менее, в этих ОС может также случиться так, что система исчерпает виртуальную память, в то время как есть все еще свободная RAM, которая является довольно запутанной и обычно неправильно понимаемой.
источник
Когда доступная оперативная память исчерпана, ядро начинает выгружать биты обработки на диск. На самом деле, ядро начинает менять местами, когда ОЗУ почти исчерпано: оно начинает активно менять местами, когда оно находится в режиме ожидания, чтобы быть более отзывчивым, если приложение внезапно требует больше памяти.
Обратите внимание, что оперативная память используется не только для хранения памяти процессов. В типичной исправной системе только около половины ОЗУ используется процессами, а другая половина используется для дискового кэша и буферов. Это обеспечивает хороший баланс между запущенными процессами и файлами ввода / вывода.
Пространство подкачки не бесконечно. В какой-то момент, если процессы продолжают выделять все больше и больше памяти, вторичные данные из ОЗУ заполнят обмен. Когда это происходит, процессы, которые пытаются запросить больше памяти, видят, что их запрос отклонен.
По умолчанию Linux перегружает память. Это означает, что иногда он позволяет запускать процесс с памятью, которую он зарезервировал, но не использовал. Основной причиной чрезмерного выполнения работ является способ разветвления . Когда процесс запускает подпроцесс, дочерний процесс концептуально работает в реплике памяти родительского процесса - два процесса изначально имеют память с одинаковым содержимым, но этот контент будет расходиться, поскольку процессы вносят изменения каждый в своем собственном пространстве. Чтобы полностью реализовать это, ядро должно было бы скопировать всю память родителя. Это замедлит процесс разветвления, поэтому ядро будет выполнять копирование при записиизначально ребенок делится всей своей памятью с родителем; всякий раз, когда какой-либо процесс записывает на общую страницу, ядро создает копию этой страницы, чтобы прервать совместное использование.
Часто ребенок оставляет много страниц нетронутыми. Если бы ядро выделило достаточно памяти для репликации пространства памяти родителя на каждой ветке, много памяти было бы потрачено впустую в резервированиях, которые дочерние процессы никогда не будут использовать. Отсюда чрезмерная загрузка: ядро резервирует только часть этой памяти, основываясь на оценке того, сколько страниц понадобится ребенку.
Если процесс пытается выделить какую-то память и не хватает памяти, процесс получает ответ об ошибке и обрабатывает его так, как считает нужным. Если процесс косвенно запрашивает память путем записи на общую страницу, которая должна быть не общей, это другая история. Невозможно сообщить об этой ситуации приложению: оно считает, что оно имеет доступные для записи данные и может даже прочитать их - просто запись требует некоторой более сложной операции под капотом. Если ядро не может предоставить новую страницу памяти, все, что он может сделать, это уничтожить запрашивающий процесс или уничтожить какой-то другой процесс, чтобы заполнить память.
В этот момент вы можете подумать, что уничтожение запрашивающего процесса - очевидное решение. Но на практике это не так хорошо. Этот процесс может быть важным, поскольку сейчас ему нужен только доступ к одной из его страниц, в то время как могут выполняться другие, менее важные процессы. Таким образом, ядро включает в себя сложную эвристику, чтобы выбрать, какие процессы нужно убить - (в) известный убийца OOM .
источник
Просто чтобы добавить другую точку зрения из других ответов, многие VPS размещают несколько виртуальных машин на любом сервере. Любая отдельная виртуальная машина будет иметь определенный объем оперативной памяти для собственного использования. Многие провайдеры предлагают «оперативную память», в которой они могут использовать оперативную память сверх того объема, который им назначен. Это предназначено только для кратковременного использования, и те, кто превышает это количество в течение продолжительного периода времени, могут быть оштрафованы хостом, убивающим процессы, чтобы снизить объем используемой оперативной памяти, чтобы другие не пострадали от хост-компьютер перегружен.
источник
Некоторое время linux занимает внешнее виртуальное пространство. Это раздел подкачки. Когда Ram заполнен, linux использует эту область подкачки для запуска процесса с низким приоритетом.
источник