Значимые и ссылочные типы
Dmitriy Makarov
Я так часто слышал вопрос:
-"А знаете ли вы чем отличаются ссылочные типы от значимых?"
Что решил что лучшей темы что бы начать нет, на самом деле ответ на него я узнал когда уже поработал в нескольких компаниях. (и да простят меня все они...хотя нет им было все= менеджеры лишь просили меня поставить тот или иной костыль, хотя некоторые из них являлись и являются IT гигантами)
Начнем с простой теории что относится к значимым, а что относится к ссылочным типам:
Значимые:
- Целочисленные типы (sbyte, byte, char, short, ushort, int, uint, long, ulong)
- Типы с плавающей точкой (float, double)
- 128 - разрядный тип данных (decimal)
- Логический тип (bool)
- Тип пользовательских структуры (struct)
- Типы перечислений (enum)
- Типы кортежей (ValueTuple)
- Типы значений, допускающие NULL (Nullable<T>)
- Тип символа (char)
Ссылочные:
- Тип класс (class)
- Тип интерфейс (interface)
- Тип делегат (delegate)
- Тип запись (record)
- Тип динамик (dynamic)
- Тип строка (string)
- Тип объект (object)
Отличие этих двух видов типов заключается в том что значимый хранится в стеке(stack), а ссылочный в куче(heap). Что же вся эта куча воды значит, давайте разбираться на простом примере:

1) Сначала мы попадаем в метод Main в котором есть метод StackExample.
2) Как только отладчик зашел в StackExamle выделяется память в стеке.
3) Когда отладчик видит переменную и знак = идет присвоение значения в стек.
4) В конце метода стек очищается и все три переменные значимого типа перестают существовать в стеке.
Теперь пример с элементом из кучи, я добавил класс MyInt и заменил 3 переменную на него.

1) Сначала мы попадаем в метод Main в котором есть метод StackExample.
2) Как только отладчик зашел в StackExamle выделяется память в стеке.
3) Когда отладчик видит переменную и знак = идет присвоение значения в стек.
4) Но на строке 16 есть ключевое слово new которое означает что память нужно выделить из кучи.
4) В конце метода стек очищается и две переменные значимого типа перестают существовать в стеке, но переменная класса MyInt находится в куче и ее пока что никто, не очистил. Очисткой кучи занимается сборщик мусора, как только на память в куче перестали ссылаться он ее очистит, как это происходит это отдельная тема...
Для лучшего понимания я запилил gif:

Подводные камни или что может пойти не так как задумано
- StackOverflowException
У стека ограниченный размер для 32-битных это 1 Mb для 64-битных это 4 Mb.
Самый простой пример того что память в стеке закончилась это зацикленная рекурсия:

Что произошло? Закончилась память в стеке, по сколько при каждом вызове метода StackOverflow мы будем добавлять в стек фрейм для этого метода с переменной и так до бесконечности (ну как до бесконечности в моем случае пока 4 Mb не заполнились).
- OutOfMemoryException
Все наши компьютеры ограничены ресурсами и важно понимать что объекты динамической памяти(кучи) имеют ограничение в виде размера ОЗУ.
Сделаем пример который может привести к данному поведению:

Каждую интеракцию этого цикла в куче будет создаваться новый объект, каждый из этих объектов будет 32 byte, допустим у нас 1 Gb что позволит нам разместить 33 554 432, обычно такая ситуация получается когда мы пытаемся загрузить построчно файл и записываем его в string(очень распространённая задача в онлайн кодинге, обычно дают код и спрашивают что здесь не так).
На этом думаю стоит остановиться, тема конечно обширная, но то что изложено в этой статье достаточно что бы понять как работает механизм стека и кучи. Пишите комментарии, если есть что добавить по данной теме.