Тип значения выделяется в стеке, если это локальная переменная в методе. Если тип значения является членом класса, он будет выделен как часть области памяти объекта в куче.
Переменная типа значения не требует каких-либо дополнительных данных для отслеживания типа, как это делают ссылочные типы. Компилятор всегда знает, где находятся переменные типа значения и каков их тип, поэтому в дополнение к фактическим данным не требуется никаких дополнительных данных. Переменная Int32 всегда будет иметь длину четыре байта.
Ссылочный тип выделяется в куче, и у него есть ссылка (или более), которая указывает на него. Сама ссылка на самом деле является типом значения, поэтому она будет просто указателем, а компилятор отслеживает, где она находится и какого она типа. Тип ссылки не должен совпадать с типом объекта, на который он указывает, поэтому объекту нужна дополнительная информация для отслеживания типа. Например, ссылка на объект, указывающая на экземпляр класса StringBuilder:
object o = new StringBuilder();
Здесь компилятор отслеживает, что тип ссылки является объектом, поэтому он будет просто указателем (4 байта в 32-разрядном приложении). Объект StringBuilder хранится в куче, и у него есть два дополнительных указателя, которые отслеживают фактический тип.
Тип значения также может быть упакован, то есть сохранен как объект в куче. Это происходит, когда вы приводите тип значения к объекту:
object p = 42;
Это позволит разместить объект в куче и скопировать в него значение целого числа. Этот объект будет нуждаться в дополнительной информации о типе, чтобы отслеживать тип, поэтому он будет использовать 12 байтов в куче вместо четырех (в 32-битном приложении).