Как я могу ограничить объем памяти, доступной для процесса?

11

Я разрабатываю программу в го; иногда это приводит к выделению очень большого объема памяти (>> 10 ГБ на машине с 8 ГБ физической памяти), в результате чего система перестает отвечать на запросы. Я хочу ограничить объем памяти, который может выделить процесс. Обычный способ сделать это:

ulimit -m 4000000 ; ./myprogram

... которая должна убить мою программу, если она пытается использовать более 4 ГБ памяти.

На OS X El Capitan это, кажется, не имеет никакого эффекта; даже ulimit -m 1(ограничение всех программ до 1 КБ памяти!) неэффективно.

Как я могу установить верхнюю границу памяти, доступной для определенного процесса?

cpcallen
источник
Когда вы говорите, как обычно, я бы это сделал , что вы имеете в виду? Точнее, когда ты обычно это делаешь? И когда вы делаете это, вы имеете в виду Mac OS X El Capitan или другую среду? Можете ли вы привести пример, когда вы успешно использовали его? По сути, я просто пытаюсь уточнить, работает ли этот процесс для вас нормально, но просто не работает для этой конкретной программы? Или у вас это вообще не работает, но вы думаете, что должны?
Monomeeth
1
Под «обычным способом я бы сделал это» я имею в виду способ, которым я сделал бы это на других разновидностях Unix. Я отмечаю, что только что обнаружил, что ulimit -mбольше не работает в Linux (> 2.4.30), хотя ulimit -vвсе еще работает как ожидалось. (Как ulimit -m, ulimit -vкажется , также не влияет на OS X.)
cpcallen
Похоже, у вас есть утечка памяти в программе, которую вы пишете, и вам нужно улучшить сборку мусора. Вы читали об управлении памятью в го?
Тодд Дабни
1
Нет, не утечка памяти - просто поиск в графе потенциально очень большого пространства состояний. Я мог бы добавить код, чтобы прервать поиск, если различные внутренние структуры становятся слишком большими, но я ожидал, что смогу сделать то же самое (не допустить заклинивания машины большими входами) с помощью однострочной оболочки.
cpcallen

Ответы:

1

Существует два подхода к ограничению использования памяти: ex post facto и превентивный. То есть вы можете попытаться убить вашу программу после того, как она станет слишком большой, или вы можете запрограммировать ее, чтобы она не стала слишком большой.

Если вы настаиваете на подходе ex post facto, вы можете использовать следующий скрипт Bash. Этот сценарий сначала находит объем памяти (определяемый «размером резидентного набора»), который использует процесс с processid pid, отфильтровывает все нечисловые данные с помощью grep и сохраняет количество как переменную n. Затем скрипт проверяет, больше ли n указанного вами x. Если это так, процесс с processid pid завершается.

Пожалуйста, обратите внимание:

  1. Вы должны заменить <pid>идентификатор процесса вашей программы.
  2. Вы должны заменить <x>на rss = "размер резидентного набора" (т. Е. Реальный объем памяти), который вы не хотите, чтобы программа превышала.

n=$(ps -<pid> -o rss | grep '[0-9]') if [ $n -gt <x> ]; then kill -9 <pid>; fi

Если вы хотите, чтобы это выполнялось каждые y секунд, просто включите его в цикл и попросите его ждать y секунд после каждой итерации. Вы также можете написать аналогичную команду, используя top. Ваша отправная точка будет top -l 1|grep "<pid>"|awk '{print $10}'.

@ kenorb в ответ помог мне с моим сценарием


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

Во-первых, вы уверены, что использование памяти действительно проблема? В Go документации говорится:

Распределитель памяти Go резервирует большую область виртуальной памяти в качестве арены для распределений. Эта виртуальная память является локальной для определенного процесса Go; резервирование не лишает другие процессы памяти.

Если вы все еще думаете, что у вас есть проблема, тогда я рекомендую вам вручную управлять памятью, как это делается на языке программирования C. Поскольку go написан на C, я подозревал, что будут способы попасть в управление / распределение памяти C, и это действительно так. Посмотрите этот репозиторий GitHub, который,

позволяет вам осуществлять ручное управление памятью через стандартный C-распределитель для вашей системы. Это тонкая оболочка поверх malloc, calloc и не содержит. Смотрите man malloc для получения подробной информации об этих функциях для вашей системы. Эта библиотека использует cgo.

Вариант использования задается как:

Зачем тебе это?

Когда программа вызывает нехватку памяти или системе не хватает памяти, может быть полезно вручную управлять выделением и освобождением памяти. Go может помочь вам контролировать распределение, но невозможно явно освободить ненужные данные.

Это кажется лучшим долгосрочным решением.

Если вы хотите узнать больше о C (включая управление памятью), язык программирования C является стандартным справочником.

Эван Росика
источник
Я уверен, что использование памяти действительно проблема. Когда программа неожиданно выросла до> 2x объема физической памяти, машина перестала отвечать на запросы из-за перегрузки страницы. Прошло около часа между тем, когда я нажал ^ C и когда macOS возобновил реагировать на щелчки мышью; в то же время единственным свидетельством того, что он не был заморожен, был тихий шум жесткого диска, который быстро тикал.
cpcallen
Это, вероятно, разумное решение на практике, но в отличие от ulimit в (не Darwin) ОС UNIX, оно зависит от способности своевременно распределять достаточно памяти для успешного выполнения команды kill. Я действительно предпочел бы иметь ограничения размера процесса, установленные ядром.
cpcallen
@cpcallen, выполняя поиск, похоже, «параметр -m для ulimit не влияет на системы Linux с версиями ядра, более поздними, чем 2.4.30». Кажется, OSX также подхватил это изменение. Попробуйте опцию -v, чтобы ограничить адресное пространство
Эван
Смотрите этот ответ superuser.com/questions/239796/limits-conf-to-set-memory-limits/...
Эван Rosica