В этой статье рассматривается стратегия освобождения памяти в Linux и то, как системный администратор может этим управлять.
Стратегия освобождения памяти в Linux
Из предыдущей статьи вы узнали что каждому процессу выделяется блок памяти (виртуальная память) и он эту память использует (физическая память). Суммарно физическая память состоит из оперативной памяти и подкачки (swap). Но подкачка может лишь хранить какие-то данные, которые не очень востребованы. Процессам же, чтобы избежать сильных тормозов, требуется работать с оперативной памятью.
При всём этом, суммарно, виртуальной памяти может быть выделено больше чем есть оперативной памяти. И может случится такое, что процессу нужна память, а взять её не откуда. В этом случае ядро может высвободить занятую память тремя способами:
- сбросить, по мнению ядра, самые ненужные данные из резидентной памяти в swap;
- сбросить некоторые файлы, которые хранятся в Page Cache, на диск;
- запустить специальный инструмент OOM Killer, который завершит самый ненужный и занимающий больше всего памяти процесс.
Есть несколько стратегий распределения памяти. Управлять этими стратегиями можно с помощью параметра sysctl — vm.overcommit_memory. Этот параметр может принимать значения 0, 1 или 2:
- vm.overcommit_memory = 2 — сумма виртуальной память всех процессов не будет превышать физическую память. При этом, каждому отдельному процессу будет выделено не больше vm.overcommit_ratio процентов от всей памяти.
- vm.overcommit_memory = 1 — виртуальной памяти может быть выделено больше чем есть физической. При этом, если приложение действительно попытается использовать эту память а физической памяти не хватит, то произойдет событие out of memory. По умолчанию, при событии out of memory, запустится OOM Killer и завершит какой-то процесс. При такой стратегии можно настроить еще некоторые параметры sysctl:
- vm.panic_on_oom = 1 — при out of memory будет происходить kernel.panic вместо запуска OOM Killer;
- kernel.panic = 10 — при kernel.panic через 10 секунд сервер пере-загрузится.
- vm.overcommit_memory = 0 — такая настройка используется по умолчанию. При этом ядро, с помощью специальных алгоритмов, само решает когда и сколько выделять памяти процессам.
Работа OOM Killer
OOM Killer — это компонент ядра Linux, призванный решать проблему недостатка памяти за счет убийства одного из существующих процессов по определённому алгоритму. В случае нехватки памяти (out of memory) он убивает процесс с помощью сигнала SIGKILL и не даёт ему корректно завершиться.
Алгоритм работы OOM Killer следующий. Все процессы, во время своей работы, накапливают специальные балы — oom_score. И при нехватке памяти OOM Killer убивает процесс с наивысшим oom_score. Посмотреть значение oom_score для процесса можно так — cat /proc/<pid>/oom_score, например:
$ cat /proc/1/oom_score 0 $ cat /proc/2383/oom_score 668
Как рассчитывается oom_score (взято от сюда):
- чем больше памяти занимает процесс, тем выше oom_score;
- чем больше у процесса дочерних процессов, тем выше oom_score;
- чем выше у процесса Nice, тем выше oom_score;
- чем раньше создан процесс, тем ниже oom_score;
- если процесс имеет прямой доступ к аппаратному обеспечению, то oom_score снижается;
- если процесс имеет root привилегии, то oom_score снижается.
Но значением oom_score можно управлять, чтобы например обезопасить некоторые процессы от OOM Killer. Для этого у процесса есть параметр oom_score_adj. Чем меньше у процесса oom_score_adj, тем меньше oom_score.
Вы можете задать параметр oom_score_adj для служб systemd, поправив файл юнита службы. Для этого в секции [Service] укажите параметр OOMScoreAdjust со значением от — 1000 до 1000. Если указать -1000, то OOM Killer никогда не убьёт процессы данной службы. А если указать 1000, то для OOM Killer процессы этой службы превратятся в главную цель.
Также, вы можете напрямую записать значение oom_score_adj для определённого процесса. Для этого нужно поправить файл /proc/<pid>/oom_score_adj.
$ cat /proc/2383/oom_score_adj 0 $ cat /proc/2383/oom_score 668 $ echo 20 | sudo tee /proc/2383/oom_score_adj $ cat /proc/2383/oom_score 682
В примере видно что вначале oom_score_adj для процесса 2383 равнялся 0, а oom_score равнялся 668. Затем я поменял значение oom_score_adj на 20 и oom_score увеличился до 682.
Итог
Вы узнали про некоторые параметры sysctl, с помощью которых можно настроить стратегию высвобождения памяти:
- vm.overcommit_memory — тип стратегии;
- vm.overcommit_ratio — процент всей памяти, который можно выделить одному процессу;
- vm.panic_on_oom — при событии out of memory выполнить kernel.panic;
- kernel.panic — перезагрузить сервер через указанное число секунд в случае kernel.panic.
Также вы узнали про инструмент OOM Killer. И про значения oom_score и oom_score_adj на которые полагается OOM Killer в своей работе.
И вы узнали что ситуация нехватки памяти называется — out of memory.