← Back to notes

NGINX: Memory
Model

2026-02-21


Теория

Принцип

Память живёт столько же, сколько request. Большая часть временных структур выделяется из пула запроса и освобождается одним действием в конце.


request memory pool

Для каждого HTTP request NGINX создаёт memory pool (r->pool):

  • мелкие объекты (структуры, строки, контексты модулей) берутся из этого пула;
  • при завершении request пул уничтожается целиком;
  • нет free() для каждого маленького объекта.

Если данные должны жить дольше request, их нельзя хранить в r->pool — используют другие контексты (например pool соединения).


slab allocator

slab используется для долгоживущей shared memory между worker:

  • limit_req_zone;
  • limit_conn_zone;
  • keys_zone у proxy_cache;
  • upstream zone.

Это другой класс памяти: общие структуры и статистика, а не один request.


ngx_buf_t

ngx_buf_t описывает кусок данных:

  • где лежат данные (RAM или файл);
  • диапазон байт (pos/last, file_pos/file_last);
  • можно ли буфер переиспользовать/отправлять/сохранять.

Обычно метаданные, а не «владелец всей памяти».


chain buffers

Тело ответа передаётся как цепочки буферов (ngx_chain_t):

  • модуль добавляет куски в chain;
  • следующий фильтр читает chain и передаёт дальше;
  • удобно для стриминга и больших ответов без копирования всего тела.

cleanup handlers

cleanup handler регистрируется в request pool и вызывается при завершении request:

  • закрыть временный файл;
  • освободить внешний ресурс;
  • отменить побочный state.

Cleanup вызывается при уничтожении pool, в том числе на аварийных путях.


Что нужно уметь объяснить

Почему нет free() на каждый объект?

Объекты запроса живут примерно одинаково долго. Вместо тысяч malloc/free — много быстрых выделений из pool и один destroy pool в конце.

Что произойдёт, если сохранить указатель вне request lifetime?

После завершения request память pool уже недействительна. Указатель становится dangling: чтение/запись через него может привести к крашу или повреждению данных.

Почему это быстрее malloc/free?

  • меньше вызовов аллокатора общего назначения;
  • меньше фрагментации;
  • лучше locality (данные request лежат рядом);
  • дешевле очистка: один pool destroy вместо множества free.

Практика

1. Увеличить client_body

По умолчанию client_max_body_size 1m. Поднимем:

http {
    client_max_body_size 100m;
    client_body_buffer_size 128k;
    client_body_temp_path /var/lib/nginx/body 1 2;
}
nginx -t && sudo nginx -s reload

2. Временные файлы

Когда тело не помещается в RAM-буфер, NGINX пишет его во временные файлы.

sudo ls -lah /var/lib/nginx/body
sudo find /var/lib/nginx/body -type f | head

3. proxy_buffering on/off

location /api/ {
    proxy_pass http://127.0.0.1:18080;
    proxy_buffering on;   # по умолчанию on; сравнить с off
}
  • on: больше буферизации в NGINX, меньше pressure от медленных клиентов;
  • off: ближе к стримингу, меньше внутренней буферизации, выше зависимость от скорости клиента.

4. Память через top

top -H -p $(pgrep -d',' -f 'nginx: worker process')

Под нагрузкой смотреть: RES/MEM% у worker, изменения при proxy_buffering on/off, рост temp-файлов при больших body.


Ссылки

NGINX: Memory Model | Aleksandr Suprun