Память В C# Стек И Куча

Скачать

📦 Память в C#: Стек и Куча

🧠 Как работает память в .NET?

Программы на C# (и .NET в целом) выполняются внутри CLR (Common Language Runtime). CLR управляет:

  • Размещением переменных в памяти
  • Жизненным циклом объектов
  • Автоматическим распределением и освобождением памяти

Память делится на две ключевые области:

  • Стек (Stack) — для временных и "лёгких" данных
  • Куча (Heap) — для объектов и всего, что создаётся динамически

📐 Стек

📌 Что это?

Stack — это область памяти, которая работает по принципу LIFO (Last In — First Out).

📍 В стеке хранятся:

  • Локальные переменные примитивных типов (int, double, bool)
  • Параметры методов
  • Ссылки на объекты в куче
  • Значимые типы (структуры)

📊 Как работает стек?

Каждый вызов метода добавляет ("вталкивает") в стек фрейм, в котором хранятся его локальные переменные и параметры. Когда метод завершает выполнение — фрейм удаляется.

Main()
┌──────────────┐
│ int a = 10;  │ ← локальная переменная
│ Call Foo()   │ ← вызов метода
└──────────────┘
      ↓
Foo()
┌──────────────┐
│ int b = 5;   │ ← переменная Foo
└──────────────┘
````

Когда `Foo` завершится, его фрейм удалится.

---

## 🧱 Куча

### 📌 Что это?

`Heap` — это область памяти для **долгоживущих объектов**, выделяемая динамически с помощью ключевого слова `new`.

### 📍 В куче хранятся:

* Объекты классов (`new Person()`)
* Массивы (`new int[1000]`)
* `string`
* Делегаты
* Всё, что создаётся через `new`

> Важно: в стеке остаётся **ссылка** на объект в куче.

---

## 💡 Пример: стек и куча вместе

```csharp
class Person {
    public string Name;
}

void Main() {
    int x = 42;                        // Значимый тип — в стеке
    Person p = new Person();          // Ссылка p — в стеке, объект — в куче
    p.Name = "Anton";                 // Строка — в куче
}

📈 Визуализация

[ Stack ]                          [ Heap ]
─────────────                     ────────────────────────────
| x = 42     |                   | Person                    |
| p ───────  | ─────── ─────── ->| Name ───────┐             |
─────────────                    └─────────────┘             ↓
                                [ string: "Anton" ] <──────┘

🧩 Ключевые отличия

Характеристика Стек (Stack) Куча (Heap)
Управление Автоматическое (при выходе из метода) Управляется CLR (через GC)
Скорость доступа Очень быстрая Медленнее
Размер Ограничен (обычно до 1 МБ) Большой (выделяется ОС)
Типы данных Значимые (Value types), ссылки Ссылочные (Reference types)
Жизненный цикл До конца метода До исчезновения всех ссылок
Пример int, bool, struct class, string, array

🔍 Ещё один пример с ASCII-схемой

struct Point {
    public int X;
    public int Y;
}

class Rectangle {
    public Point TopLeft;
    public Point BottomRight;
}
void Main() {
    Rectangle r = new Rectangle();
    r.TopLeft = new Point { X = 0, Y = 0 };
    r.BottomRight = new Point { X = 10, Y = 10 };
}
[ Stack ]
───────────────
| r ─────────┐ |
───────────────
             ↓
[ Heap ]
────────────────────────────────────
| Rectangle                        |
| TopLeft: {X=0,Y=0}               |
| BottomRight: {X=10,Y=10}        |
────────────────────────────────────

🔎 Обрати внимание: хотя Point — структура (значимый тип), в куче они внутри объекта Rectangle.


🔄 Как взаимодействуют значимые и ссылочные типы

🧩 Значимые типы внутри ссылочных

Если значимый тип (например, struct) находится внутри ссылочного типа (class), то:

  • Вся структура становится частью объекта, который хранится в куче
  • Копирование объекта не копирует ссылку на структуру отдельно — она встроена

Пример:

struct Point {
    public int X, Y;
}

class Figure {
    public Point Center;
}

var fig = new Figure();
fig.Center.X = 5;

В памяти:

[ Stack ]
──────────────
| fig ──────┐ |  ← ссылка на Figure
──────────────
            ↓
[ Heap ]
─────────────────────────────
| Figure                    |
| Center: { X=5, Y=0 }      |  ← структура внутри объекта
─────────────────────────────

Point здесь не имеет своей "ссылки". Он физически встроен внутрь объекта Figure.


🔁 Ссылочные типы внутри значимых

Если в структуре есть поле ссылочного типа (например, string, class), то:

  • Сама структура хранится в стеке (или как часть другого объекта, если вложена)
  • Но ссылка внутри неё указывает в кучу

Пример:

struct Message {
    public string Text;
}

void Main() {
    Message msg = new Message();
    msg.Text = "Hello";
}

В памяти:

[ Stack ]
──────────────────
| msg.Text ────┐  | ← структура целиком в стеке
──────────────────
               ↓
[ Heap ]
──────────────
| "Hello"     | ← строка — ссылочный тип
──────────────

🧠 Копирование: Важное различие

Point p1 = new Point { X = 1, Y = 2 };
Point p2 = p1;         // Копируется ЗНАЧЕНИЕ
p2.X = 99;

Console.WriteLine(p1.X); // 1 — не изменилось!
Person a = new Person { Name = "Anton" };
Person b = a;           // Копируется ССЫЛКА
b.Name = "John";

Console.WriteLine(a.Name); // John — объект один!
Тип Поведение при присваивании
struct Копируется полностью
class Копируется ссылка на один и тот же объект

📌 Итого

Сценарий Где хранятся данные
struct в class В куче, как часть объекта
class в struct Сама структура в стеке, ссылка — в поле
struct в стеке (локально) Целиком в стеке
class в стеке (как ссылка) Только ссылка в стеке, объект — в куче
Присваивание struct Копия
Присваивание class Ссылка

🔎 Это ключевое различие определяет поведение при передаче по значению и ссылке, а также влияет на производительность.


📝 Резюме

  • Стек — быстрый, небольшой, используется для временных и примитивных данных
  • Куча — гибкий, подходит для хранения объектов, управляется CLR
  • Ссылки на объекты всегда хранятся в стеке, сами объекты — в куче
  • Понимание различий важно для написания эффективного и предсказуемого по памяти кода

📌 Тема работы сборщика мусора (Garbage Collector) будет рассмотрена отдельно — она критически важна для работы с кучей, но требует детального внимания.