# Virtual memory - виртуальные адреса, page tables _Процессы и ресурсы · LinuxLab Knowledge Base_ **TL;DR:** Каждый процесс видит свой 64-битный virtual address space. MMU переводит виртуальные адреса в физические через page tables. Это основа изоляции и mmap. ## Зачем вообще Без virtual memory все процессы сидели бы в одной плоской памяти и ломали друг друга. Виртуальные адреса дают: 1. **Изоляцию** - процесс не дотянется до памяти соседа 2. **Иллюзию большой памяти** - каждому процессу кажется что у него 2^48 байт; реально маппится только используемое 3. **Lazy allocation** - `malloc(1GB)` не выделяет физическую память, пока ты туда не запишешь 4. **Shared mappings** - одна физическая страница = в адресных пространствах разных процессов (общие библиотеки, [mmap](/kb/mmap.md)) 5. **Swap и page cache** - страница может физически жить на диске или в кеше ## Page и page table Память управляется страницами (обычно 4 KB, бывает 2 MB / 1 GB - «huge pages»). MMU при каждом обращении: 1. Берёт virtual address из CPU 2. Идёт в **page table** процесса (это структура в физической памяти) 3. Находит запись для этой страницы → physical address + flags (rw/x, present) 4. Если страницы нет → **page fault** → ядро вмешивается TLB (Translation Lookaside Buffer) - кеш недавних переводов в самом MMU. TLB miss дорогой, поэтому huge pages помогают memory-intensive workload'ам. ## Виды page fault - **Minor fault** - страница есть в RAM (например, в [page-cache](/kb/page-cache.md)), просто не была привязана к этому процессу. Ядро правит page table - быстро. - **Major fault** - страницу надо подгрузить с диска ([swap](/kb/swap.md) или mmap-файл). Реальный disk I/O - сотни/тысячи раз медленнее. - **Segfault (SIGSEGV)** - обращение к адресу, которого вообще нет в карте процесса. Программа падает с ядром. ```bash ps -o min_flt,maj_flt -p # счётчики minor/major faults процесса vmstat 1 # в колонках si/so - swap-in/swap-out (major faults в swap) ``` Высокий `maj_flt` без swap = mmap-файлы со случайным доступом (БД, large datasets). ## Карта процесса: /proc//maps ```bash 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]` - анонимные регионы. Вот ВСЁ что видит процесс. ```bash pmap -x # удобный вид той же информации с размерами cat /proc//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](/kb/oom-killer.md)) смотрит на RSS, не VSZ. ## Memory commit и overcommit Когда `malloc()` возвращает указатель, физической памяти ещё нет. Linux может **overcommit** - выдавать больше виртуальной памяти чем physical+swap. ```bash 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: ```bash 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 в БД): ```bash cat /sys/kernel/mm/transparent_hugepage/enabled # always / madvise / never ``` ## Дебаг - `pmap -x ` - карта с размерами - `smem` - RSS / PSS / USS по процессам (PSS = proportional, делит shared) - `cat /proc//smaps` - детальная инфа на каждый регион - `vmstat 1` - pages in/out, swap, free - `slabtop` - kernel slab allocator (структуры ядра) ## Команды ```bash cat /proc/$$/maps | head ``` Карта виртуальной памяти процесса - сегменты, либы, heap, stack ```bash pmap -x ``` Удобный вид /proc//maps с размерами и RSS по регионам ```bash cat /proc//status | grep ^Vm ``` VmSize/VmRSS/VmSwap процесса - RSS показывает реальное потребление ```bash cat /proc/meminfo | grep -E 'MemTotal|MemAvailable|HugePages' ``` Общая память + доступная (учитывает page cache) + huge pages ```bash smem -tk -P python ``` RSS/PSS/USS по python-процессам - PSS правильнее когда есть shared ## См. также - [Процесс и PID](/kb/process-and-pid.md) - [Swap - когда RAM кончается](/kb/swap.md) - [Page cache - диск в памяти](/kb/page-cache.md) - [mmap - файлы и shared memory](/kb/mmap.md)