Меня путают основы распределения памяти между стеком и кучей . Согласно стандартному определению (то, что все говорят), все типы значений будут размещены в стеке, а ссылочные типы будут помещены в кучу .
Теперь рассмотрим следующий пример:
class MyClass
{
int myInt = 0;
string myString = "Something";
}
class Program
{
static void Main(string[] args)
{
MyClass m = new MyClass();
}
}
Теперь, как будет происходить выделение памяти в C #? Будет ли объект MyClass
(то есть m
) полностью размещен в куче? То есть int myInt
и string myString
в кучу пойдут оба?
Или объект будет разделен на две части и будет размещен в обоих местах памяти, то есть в стеке и куче?
Ответы:
m
размещается в куче, включаяmyInt
. Ситуации, когда примитивные типы (и структуры) размещаются в стеке, возникают во время вызова метода, который выделяет место для локальных переменных в стеке (потому что это быстрее). Например:class MyClass { int myInt = 0; string myString = "Something"; void Foo(int x, int y) { int rv = x + y + myInt; myInt = 2^rv; } }
rv
,x
,y
Все будет в стеке.myInt
находится где-то в куче (и должен быть доступен черезthis
указатель).источник
Вы должны рассмотреть вопрос о том, где размещаются объекты, в качестве детали реализации. Для вас не имеет значения, где именно хранятся биты объекта. Может иметь значение, является ли объект ссылочным типом или типом значения, но вам не нужно беспокоиться о том, где он будет храниться, пока вы не начнете оптимизацию поведения сборки мусора.
В то время как ссылочные типы всегда выделяются в куче в текущих реализациях, типы значений могут быть размещены в стеке, но не обязательно. Тип значения выделяется в стеке только в том случае, если это распакованная неэкранирующая локальная или временная переменная, которая не содержится в ссылочном типе и не размещена в регистре.
Я что-то пропустил?
Конечно, было бы упущением, если бы я не дал ссылку на сообщения Эрика Липперта по этой теме:
источник
«Все типы VALUE будут размещены в стеке» - это очень и очень неправильно; Переменные структуры могут находиться в стеке как переменные метода. Однако поля в типе живут с этим типом . Если тип объявления поля является классом, значения находятся в куче как часть этого объекта. Если тип объявления поля является структурой, поля являются частью этой структуры, где бы она ни находилась.
Даже переменные метода могут находиться в куче, если они захвачены (лямбда / анон-метод), или являются частью (например) блока итератора.
источник
object x = 12;
метод, 12 будут сохранены в куче, даже если это целое число (тип значения).null
, либо ссылку на объект кучи соответствующего типа. Для каждого типа значения существует соответствующий тип объекта-кучи; попытка сохранить тип значения в месте хранения ссылочного типа создаст новый объект соответствующего ему типа объекта кучи, скопирует все поля в этот новый объект и сохранит ссылку на объект в месте хранения ссылочного типа. C # делает вид, что тип значения и тип объекта совпадают, но ...List<T>.Enumerator
который хранится в переменной этого типа, будет демонстрировать семантику значения, потому что это тип значения. Однако A,List<T>.Enumerator
который хранится в переменной типаIEnumerator<T>
, будет вести себя как ссылочный тип. Если рассматривать последних как тип, отличный от первого, различие в поведении легко объяснимо. Притворяться, что они одного типа, гораздо сложнее рассуждать о них.Отличное объяснение:
Часть 1: http://www.c-sharpcorner.com/uploadfile/rmcochran/csharp_memory01122006130034pm/csharp_memory.aspx
Часть 2: http://www.c-sharpcorner.com/UploadFile/rmcochran/csharp_memory2B01142006125918PM/csharp_memory2B.aspx
Часть 3: http://www.c-sharpcorner.com/UploadFile/rmcochran/chsarp_memory401152006094206AM/chsarp_memory4.aspx
Часть 4: http://www.c-sharpcorner.com/uploadfile/rmcochran/csharp_memory_401282006141834pm/csharp_memory_4.aspx
источник
Стек
Рассмотрим следующий метод:
public static int Factorial (int x) { if (x == 0) { return 1; } return x * Factorial (x - 1); }
Этот метод рекурсивен, то есть вызывает сам себя. Каждый раз, когда вводится метод, в стеке выделяется новый int , и каждый раз, когда метод завершается, int освобождается .
Куча
Рассмотрим следующий метод:
using System; using System.Text; class Test { public static void Main() { StringBuilder ref1 = new StringBuilder ("object1"); Console.WriteLine (ref1); // The StringBuilder referenced by ref1 is now eligible for GC. StringBuilder ref2 = new StringBuilder ("object2"); StringBuilder ref3 = ref2; // The StringBuilder referenced by ref2 is NOT yet eligible for GC. Console.WriteLine (ref3); // object2 } }
В приведенном выше примере мы начинаем с создания объекта StringBuilder, на который ссылается переменная ref1, а затем записываем его содержимое. Этот объект StringBuilder сразу же получает право на сборку мусора, потому что впоследствии его ничто не использует. Затем мы создаем еще один StringBuilder, на который ссылается переменная ref2, и копируем эту ссылку в ref3. Несмотря на то, что после этого момента ref2 не используется, ref3 сохраняет тот же объект StringBuilder живым, гарантируя, что он не станет подходящим для сбора, пока мы не закончим использование ref3.
источник
простые меры
Тип значения может быть сохранен в СТЕКЕ, это деталь реализации, которую можно присвоить какой-нибудь футуристической структуре данных.
Итак, лучше понять, как работает значение и ссылочный тип. Тип значения будет скопирован по значению, что означает, что когда вы передаете тип значения в качестве параметра в ФУНКЦИЮ, чем он будет скопирован по своей природе, это означает, что у вас будет полная новая копия .
Типы ссылок передаются по ссылке (опять же, не считайте, что ссылка снова будет хранить адрес в некоторых будущих версиях, он может быть сохранен в некоторых других структурах данных).
так что в вашем случае
myInt - это int, который эккапсулирован в классе, который имеет ссылочный тип, поэтому он будет привязан к экземпляру класса, который будет храниться в «THE HEAP».
Я бы посоветовал вам начать читать блоги, написанные Эриком Липпертсом.
Блог Эрика
источник
Каждый раз, когда в нем создается объект, он попадает в область памяти, известную как куча. Примитивные переменные, такие как int и double, выделяются в стеке, если они являются переменными локального метода, и в куче, если они являются переменными-членами. В методах локальные переменные помещаются в стек при вызове метода, а указатель стека уменьшается после завершения вызова метода. В многопоточном приложении каждый поток будет иметь свой собственный стек, но совместно использовать одну и ту же кучу. Вот почему в вашем коде следует проявлять осторожность, чтобы избежать проблем с одновременным доступом в пространстве кучи. Стек является потокобезопасным (каждый поток будет иметь свой собственный стек), но куча не является потокобезопасной, если не защищена синхронизацией через ваш код.
Эта ссылка также полезна http://www.programmerinterview.com/index.php/data-structures/difference-between-stack-and-heap/
источник
m - это ссылка на объект MyClass, поэтому m хранится в стеке основного потока, а объект MyClass хранится в куче. Поэтому myInt и myString хранятся в куче. Обратите внимание, что m является только ссылкой (адресом памяти) и находится в основном стеке. когда m освобожден, GC очищает объект MyClass из кучи. Подробнее читайте все четыре части этой статьи https://www.c-sharpcorner.com/article/C-Sharp-heaping-vs-stacking-in-net- часть-я /
источник