Вопрос или проблема
htop
и top
показывают большее потребление резидентной памяти, чем физическая память, доступная на машине:
вывод htop:
вывод top:
вывод free:
вывод pmap:
https://gist.github.com/ixaxaar/1571308666360f65dc66
Как это вообще возможно?
Как вы пишете в комментариях
мы делаем много mmap’ов
вероятное объяснение – это использование mmap()
для файлов на диске.
Воспроизведение
Сначала создайте файл размером 2 ГБ (он также может быть разреженным файлом, чтобы действительно не занимать 2 ГБ на диске):
truncate --size 2G 2-gb-file
Теперь откройте его с помощью mmap на Python:
test.py
:
import mmap
file = "2-gb-file"
with open(file, "r+b") as f:
mm = mmap.mmap(f.fileno(), 0)
print("Чтение файла в память...")
mm.read()
input("Нажмите Enter для завершения...")
Запустите команду python3 test.py
.
После того как mm.read()
выполнится и возвращаемое значение (прочитанные байты
в памяти) будет очищено сборщиком мусора, вы можете увидеть в htop
, что RES
значительно выше системного Mem
в использовании (2057M
против 846M
):
Скриншот в цвете:
Тот же скриншот в текстовом виде:
Mem[||||||||||||||||||||| 846M/252G] Задачи: 62, 62 thr, 141 kthr; 1 выполняется
Swp[ 0K/0K] Среднее загрузки: 0.45 0.47 0.58
Время работы: 89 дней, 09:42:34
PID USER PRI NI VIRT RES SHR S CPU%▽MEM% ЧТЕНИЕ ДИСКА ЗАПИСЬ ДИСКА ВРЕМЯ+ Команда
2476802 root 20 0 2273M 2057M 2053M S 0.0 0.8 0.00 B/s 0.00 B/s 0:00.99 python3 test.py
Объяснение
Согласно https://techtalk.intersec.com/2013/07/memory-part-2-understanding-process-memory/
RES
– это размер резидентного набора, то есть количество физической памяти, которую ядро считает назначенной процессу.[…]
Размер резидентного набора вычисляется ядром как сумма двух счётчиков. Первый счётчик содержит количество анонимных резидентных страниц (
MM_ANONPAGES
), второй – количество страниц, поддерживаемых файлами (MM_FILEPAGES
). Некоторые страницы могут считаться резидентными для более чем одного процесса одновременно, поэтому суммаRES
может быть больше фактически используемого объёма оперативной памяти или даже больше объёма оперативной памяти, доступной в системе.
В этой формулировке обоснование “больше объёма оперативной памяти в системе” заключается в том, что “некоторые страницы могут считаться более чем для одного процесса”.
Но возможно ли создать эту ситуацию с помощью всего лишь одного процесса?
Да! Смотрите ниже.
Использование больше системной памяти с помощью одного процесса
Давайте многократно откроем один и тот же файл, создавая разные отображения памяти из разных дескрипторов файлов.
Это позволяет нам создавать произвольное количество RES
:
import mmap
file = "2-gb-file"
open_files = []
mmaps = []
for i in range(200):
print("Чтение файла в память, итерация", i)
f = open(file, "r+b")
mm = mmap.mmap(f.fileno(), 0)
mm.read()
# Предотвращаем GC FD и отображение
open_files.append(f)
mmaps.append(mm)
input("Нажмите Enter для завершения...")
htop
показывает, что этот единственный процесс теперь использует 400 ГБ RES
, хотя на машине всего 252
ГБ оперативной памяти:
Скриншот в цвете:
Тот же скриншот в текстовом виде:
Mem[||||||||||||||||||||| 1.61G/252G] Задачи: 63, 62 thr, 141 kthr; 2 выполняются
Swp[ 0K/0K] Среднее загрузки: 1.47 0.87 0.68
Время работы: 89 дней, 09:49:37
PID USER PRI NI VIRT RES SHR S CPU%▽MEM% ЧТЕНИЕ ДИСКА ЗАПИСЬ ДИСКА ВРЕМЯ+ Команда
2477632 root 20 0 400G 400G 400G S 0.0 158. 0.00 B/s 0.00 B/s 3:10.91 python3 test.py
Итог
Да, с помощью mmap
мы можем сделать так, чтобы RES
превышала доступную физическую память.
Не обязательно использовать несколько процессов для этого.
Ответ или решение
Когда вы наблюдаете, что утилиты htop или top показывают использование резидентной памяти, превышающее физическую память, установленную на машине, это может вызвать недоумение. Давайте глубже разберемся в этом явлении.
Что такое резидентная память?
Резиденская память (RES) представляет собой объем физической памяти, который активно используется процессом в данный момент времени. Она включает в себя как анонимные страницы (например, данные, динамически выделенные в процессе), так и страницы, привязанные к файловым объектам (например, используемым при работе с файлами на диске).
Почему значения RES могут превышать физическую память?
-
Механизм памяти Unix/Linux:
В системах на базе Unix/Linux память управляется по страничному принципу. Это значит, что страницы могут быть перенесены в swap (виртуальная память на диске), если физическая память заполнена. Но даже в этом случае определенные страницы остаются "резидентными", т.е. система считает их частью активной памяти процесса. -
Множественные отображения (mmap):
Процессы могут использоватьmmap()
для отображения файлов в память. Когда файл отображается в память, он не обязательно загружается полностью в физическую память. Однако, если процесс создаёт множество отображений одного и того же файла (каждое из которых может рассматриваться как отдельная запись резидентной памяти), это может привести к ситуации, когда резидентная память существенно превышает физическую. -
Совместное использование страниц:
Некоторые страницы могут быть общими для нескольких процессов. Однако даже в данном случае одна активная память может быть отображена как используемая несколькими процессами, что также может привести к путанице о фактическом использовании памяти.
Пример
Для наглядного примера давайте рассмотрим следующее:
-
Сформируем 2 ГБ файл с помощью командной строки:
truncate --size 2G 2-gb-file
-
Используем Python для многократного отображения файла в память через
mmap
:import mmap file = "2-gb-file" open_files = [] mmaps = [] for i in range(200): f = open(file, "r+b") mm = mmap.mmap(f.fileno(), 0) mm.read() open_files.append(f) mmaps.append(mm) input("Press Enter to finish...")
Запустив этот код, утилиты htop или top могут показать, что резидентная память процесса превосходит физическую память системы, даже если на самом деле в физической памяти нет столько данных.
Заключение
В заключение, важно понять, что результаты htop
и top
могут варьироваться в зависимости от настроек процессов и метода управления памятью в операционной системе. Возможность резидентной памяти превышать общую физическую память системы — это не ошибка, а следствие особенностей работы с памятью в Unix-подобных системах. Подобные ситуации требуют внимательного подхода к управлению памятью и понимания, как именно работают механизмы управления памятью в вашей системе.