Зачем вообще
Без virtual memory все процессы сидели бы в одной плоской памяти и ломали друг друга. Виртуальные адреса дают:
- Изоляцию - процесс не дотянется до памяти соседа
- Иллюзию большой памяти - каждому процессу кажется что у него 2^48 байт; реально маппится только используемое
- Lazy allocation -
malloc(1GB)не выделяет физическую память, пока ты туда не запишешь - Shared mappings - одна физическая страница = в адресных пространствах разных процессов (общие библиотеки, mmap)
- Swap и page cache - страница может физически жить на диске или в кеше
Page и page table
Память управляется страницами (обычно 4 KB, бывает 2 MB / 1 GB - «huge pages»). MMU при каждом обращении:
- Берёт virtual address из CPU
- Идёт в page table процесса (это структура в физической памяти)
- Находит запись для этой страницы → physical address + flags (rw/x, present)
- Если страницы нет → page fault → ядро вмешивается
TLB (Translation Lookaside Buffer) - кеш недавних переводов в самом MMU. TLB miss дорогой, поэтому huge pages помогают memory-intensive workload'ам.
Виды page fault
- Minor fault - страница есть в RAM (например, в page-cache), просто не была привязана к этому процессу. Ядро правит page table - быстро.
- Major fault - страницу надо подгрузить с диска (swap или mmap-файл). Реальный disk I/O - сотни/тысячи раз медленнее.
- Segfault (SIGSEGV) - обращение к адресу, которого вообще нет в карте процесса. Программа падает с ядром.
ps -o min_flt,maj_flt -p <pid> # счётчики minor/major faults процесса
vmstat 1 # в колонках si/so - swap-in/swap-out (major faults в swap)
Высокий maj_flt без swap = mmap-файлы со случайным доступом
(БД, large datasets).
Карта процесса: /proc/<pid>/maps
cat /proc/self/maps | head
# 55ee87000000-55ee87004000 r--p 00000000 fc:01 1234567 /usr/bin/cat
# 55ee87004000-55ee87008000 r-xp 00004000 fc:01 1234567 /usr/bin/cat
# 55ee87008000-55ee8700a000 r--p 00008000 fc:01 1234567 /usr/bin/cat
# 7f1abcde0000-7f1abce00000 r--p 00000000 fc:01 7654321 /lib/x86_64.../libc.so.6
# ...
Колонки: virtual range / perms (rwxp/s) / offset / device / inode / path.
[heap], [stack], [vdso] - анонимные регионы. Вот ВСЁ что видит процесс.
pmap -x <pid> # удобный вид той же информации с размерами
cat /proc/<pid>/status | grep -E '^Vm'
# VmSize - общий VAS (виртуальный, не реальная память)
# VmRSS - Resident Set Size (реально в RAM прямо сейчас)
# VmData - heap + анонимные mmap'ы
# VmSwap - сколько ушло в swap
RSS vs VSZ - главное недопонимание
- VSZ (virtual size) - сколько процесс зарезервировал (включая lazy)
- RSS - сколько реально занято в физической RAM прямо сейчас
Программа на Java может показывать VSZ=10 GB, RSS=300 MB - это нормально. JVM выделяет heap «впрок», но физически использует мало.
В контейнерах OOM killer (oom-killer) смотрит на RSS, не VSZ.
Memory commit и overcommit
Когда malloc() возвращает указатель, физической памяти ещё нет.
Linux может overcommit - выдавать больше виртуальной памяти чем
physical+swap.
cat /proc/sys/vm/overcommit_memory
# 0 - heuristic (default): обычно даёт; иногда отказывает
# 1 - always: давать всем; OOM-killer разрулит
# 2 - never: жёсткий лимит = swap + RAM*overcommit_ratio
cat /proc/sys/vm/overcommit_ratio # обычно 50, при mode=2
Mode 2 (never) - для прод-БД и критичных систем: лучше получить ENOMEM и обработать, чем неожиданный OOM-kill.
Huge pages
4 KB страниц мало когда процесс работает с гигабайтами - TLB переполняется. Huge pages (2 MB и 1 GB) уменьшают TLB pressure:
cat /proc/meminfo | grep Huge # сколько huge pages в системе
echo 1024 | sudo tee /proc/sys/vm/nr_hugepages # резервировать 1024 страницы по 2MB
Применяется в БД (Postgres huge_pages=on), JVM (-XX:+UseLargePages),
DPDK / kernel-bypass networking.
Transparent huge pages (THP) - ядро автоматически склеивает 4KB-страницы в 2MB. Полезно для одних, вредно для других (jitter в БД):
cat /sys/kernel/mm/transparent_hugepage/enabled
# always / madvise / never
Дебаг
pmap -x <pid>- карта с размерамиsmem- RSS / PSS / USS по процессам (PSS = proportional, делит shared)cat /proc/<pid>/smaps- детальная инфа на каждый регионvmstat 1- pages in/out, swap, freeslabtop- kernel slab allocator (структуры ядра)