как я понимаю, int является типом значения и поэтому живет в стеке
Ваше понимание неверно. Типы значений называются «типами значений», потому что они копируются по значению. Ссылочные типы называются «ссылочными типами», потому что они копируются по ссылке. Совсем не правда, что «типы значений всегда живут в стеке». Если бы это было правдой, их бы назвали «типами стека» и «типами кучи».
Правда в том, что это деталь реализации. Различные реализации фреймворка могут использовать стек и кучу по своему усмотрению. Вот как это делает реализация Microsoft:
- значение переменной ссылочного типа является ссылкой на кучную память. Ссылка - это 32-битное или 64-битное целое число.
- значение переменной типа value равно ее значению.
- значения локальных переменных хранятся в стеке, если только локальные переменные не находятся в блоке итератора или не являются закрытыми внешними переменными анонимного метода или лямбда-выражения. В этих случаях значения локальных переменных хранятся в куче. Если, конечно, локальные переменные не могут быть оптимизированы, в этом случае нет вообще никакой памяти. Или, возможно, они могут быть зарегистрированы, и в этом случае они не находятся ни в стеке, ни в куче, они находятся в регистрах процессора.
- значения переменных экземпляра ссылочных типов и статических переменных хранятся в куче.
Это ясно?
указывает на тип значения, но не является ли это ссылкой (в куче)?
Поле «А» имеет тип значения. Это поле, и, следовательно, эта переменная хранится в куче.
при создании экземпляра Class1 его типы полей также создаются в куче?
Хранилище для переменных экземпляра находится в куче, да.
Но тогда я не понимаю, когда он действительно окажется в стеке, так как почти всегда вам нужно создать экземпляр объекта, чтобы использовать его в полях.
Это никогда не будет в стеке. Как я уже говорил выше, в стеке находятся только локальные переменные (и сгенерированные компилятором временные переменные), которые не являются замкнутыми локальными переменными лямбда-выражения или анонимного метода и не находятся в блоке итератора. И, конечно, джиттер свободен, чтобы полностью исключить их из стека и поместить их в регистры, если есть свободные регистры.
Но на самом деле, я должен спросить, почему вас волнует, что идет в стек, а что в кучу? То, что идет в стек - это то, что мы можем дешево положить в стек; все остальное уходит в кучу.