Память: стек, куча, виртуальная память
↑ 1.2 ОС и Linux для разработчика · ← Предыдущая · → Следующая
Зачем это на собесе
Без понимания стека, кучи и виртуальной памяти не объяснить value и reference типы, boxing, GC,
Span<T>и OOM-kill контейнера. Это фундамент для всего раздела .NET runtime.
Подтемы
- Виртуальная память, RSS
- Стек
- Куча
- Где что лежит в .NET
- Память в контейнере
Объяснение
Виртуальная память
Каждый процесс видит своё линейное адресное пространство. ОС и MMU отображают виртуальные страницы (обычно 4 КБ) на физическую RAM или своп. Отсюда:
- изоляция процессов;
- память выделяется «лениво»: зарезервировать ≠ использовать физически;
- RSS (resident set size) — реально занятая RAM; именно её ограничивает cgroup в контейнере.
Стек
- Отдельный на каждый поток, фиксированного размера (в .NET ~1 МБ для основного и пул-потоков, настраивается).
- Хранит кадры вызовов: локальные переменные, параметры, адрес возврата.
- Выделение и освобождение мгновенное (сдвиг указателя), очищается автоматически при выходе из метода.
- Переполнение (глубокая или бесконечная рекурсия) →
StackOverflowException, который в .NET нельзя перехватить: процесс падает.
Куча (heap)
- Общая для всех потоков процесса.
- Объекты живут, пока на них есть ссылки. В .NET освобождением занимается GC (managed heap).
- Выделение в .NET быстрое (bump pointer в текущем сегменте), но сборка мусора стоит времени и может ставить потоки на паузу.
Где что лежит в .NET (упрощённо)
- Reference types (
class, массивы, строки, делегаты) — объект всегда в куче, в переменной (на стеке или в поле) — ссылка. - Value types (
int,struct,DateTime,Guid) — там, где объявлены: локальная переменная — на стеке (или в регистре), поле класса — внутри объекта в куче, элемент массива — внутри массива в куче. - Утверждение «value types всегда на стеке» — ошибка, которую любят проверять на собесах.
struct Point { public int X, Y; } // value type
class Patient { public Point Location; } // Location хранится ВНУТРИ объекта Patient в куче
void Demo()
{
int count = 5; // стек
Point p = new() { X = 1 }; // стек (сама структура)
var patient = new Patient(); // ссылка на стеке, объект в куче
object boxed = count; // boxing: копия int упакована в объект в куче
}Память в контейнере
- Лимит памяти задаёт cgroup. При превышении ядро убивает процесс (OOMKilled, exit code 137).
- .NET учитывает лимит контейнера:
GCHeapHardLimitпо умолчанию ~75% лимита. - Утечки и большие буферы (загрузка файла целиком в
byte[]) — частая причина OOMKilled.
Нюансы и подводные камни
- Большие файлы (PDF, выгрузки) обрабатывайте потоками (
Stream), а не целиком в память: иначе Large Object Heap и риск OOM. stackallocиSpan<T>позволяют работать с буферами на стеке без аллокаций в куче, но размер стека ограничен.- Замер памяти:
GC.GetTotalMemoryпоказывает только управляемую кучу. RSS процесса больше (нативные буферы, код, стеки). - Swap в контейнерах обычно отключён: превышение лимита сразу ведёт к OOMKilled.
Вопросы с ответами
Чем стек отличается от кучи?
Стек — свой у каждого потока, быстрый, для кадров вызовов и локальных значений, освобождается при выходе из метода. Куча — общая для потоков, для объектов с произвольным временем жизни, в .NET управляется GC.
Правда ли, что value types всегда хранятся на стеке?
Нет. Value type хранится там, где объявлен: локальная переменная — обычно на стеке, поле класса или элемент массива — в куче внутри объекта, захваченная замыканием переменная — в куче.
Что такое виртуальная память?
Абстракция, при которой каждый процесс видит своё адресное пространство, а ОС отображает страницы на физическую память. Даёт изоляцию, ленивое выделение и своп.
Почему сервис в контейнере получает OOMKilled?
Реальное потребление памяти (RSS) превысило лимит cgroup. Причины: утечки, загрузка больших данных целиком, слишком высокий лимит кучи GC относительно лимита контейнера, много потоков.
Связанные темы
- Предыдущая: Процессы и потоки, контекстное переключение · Следующая: Файловая система, права, дескрипторы
- Value и reference типы: Типы: value и reference, struct и class, CTS
- Стек, куча, boxing: Стек и куча, boxing и unboxing
- GC: Сборщик мусора: поколения, LOH, режимы GC
- Span и Memory: Span и Memory: stackalloc, ref struct
- Память в JS: Память и сборщик мусора, утечки