Распределение памяти: стек против кучи? - PullRequest
75 голосов
/ 20 декабря 2010

Я путаюсь с основами выделения памяти между Stack vs Heap . Согласно стандартному определению (то, что все говорят), все Типы значений будут распределены на Stack и Reference Типы попадут в кучу .

Теперь рассмотрим следующий пример:

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 оба пойдут в кучу?

Или объект будет разделен на две части и будет выделен обеим ячейкам памяти, а именно, стеку и куче?

Ответы [ 8 ]

59 голосов
/ 20 декабря 2010

Вы должны рассмотреть вопрос , где объекты выделяются как детали реализации. Для вас не имеет значения, где именно хранятся биты объекта. Может иметь значение, является ли объект ссылочным типом или типом значения, но вам не нужно беспокоиться о том, где он будет храниться, пока не начнете оптимизировать поведение сборки мусора.

Хотя ссылочные типы всегда выделяются в куче в текущих реализациях, типы значений могут размещаться в стеке, но не обязательно. Тип значения распределяется в стеке только в том случае, если это не распакованная локальная или временная переменная, которая не содержится в ссылочном типе и не размещена в регистре.

  • Если тип значения является частью класса (как в вашем примере), он окажется в куче.
  • Если он упакован, он окажется в куче.
  • Если он находится в массиве, он окажется в куче.
  • Если это статическая переменная, она окажется в куче.
  • Если он захвачен замыканием, он окажется в куче.
  • Если он используется в итераторе или асинхронном блоке, он окажется в куче.
  • Если он создан с помощью небезопасного или неуправляемого кода, он может быть размещен в любой структуре данных (не обязательно в стеке или куче).

Я что-то пропустил?

Конечно, было бы упущением, если бы я не ссылался на сообщения Эрика Липперта по теме:

52 голосов
/ 20 декабря 2010

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).

20 голосов
/ 20 декабря 2010

«Все типы VALUE будут распределены в стек» - это очень, очень неправильно; Переменные структуры могут жить в стеке как переменные метода. Однако поля типа живут с этим типом . Если декларирующий тип поля является классом, значения находятся в куче как part этого объекта. Если декларирующий тип поля является структурой, поля являются частью этой структуры где-либо , которая живет в структуре.

Даже переменные метода могут находиться в куче, если они захвачены (лямбда / анон-метод) или частью (например) блока итератора.

1 голос
/ 28 января 2018

Stack

stack - это блок памяти для хранения local variables и parameters. Стек логически растет и сжимается при входе и выходе из функции.

Рассмотрим следующий метод:

public static int Factorial (int x)
{
    if (x == 0) 
    {
        return 1;
    }

    return x * Factorial (x - 1);
}

Этот метод рекурсивный, то есть он вызывает сам себя. Каждый раз, когда вводится метод, в стеке выделяется новое значение int , а при каждом выходе из метода значение int освобождается .


Heap

  • Куча - это блок памяти, в котором находится objects (т. Е. reference-type instances). Всякий раз, когда создается новый объект, он размещается в куче, и возвращается ссылка на этот объект. Во время выполнения программы куча начинает заполняться при создании новых объектов. Среда выполнения имеет сборщик мусора, который периодически освобождает объекты из кучи, поэтому ваша программа не запускается Out Of Memory. Объект может быть освобожден, если на него не ссылается ничего, что само по себе alive.
  • В куче также хранится static fields. В отличие от объектов, размещенных в куче (которые могут собираться мусором), these live until the application domain is torn down.

Рассмотрим следующий метод:

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.

Экземпляры типа значения (и ссылки на объекты) живут везде, где была переменная объявлен. Если экземпляр был объявлен как поле внутри типа класса или как элемент массива, этот экземпляр находится в куче.

1 голос
/ 06 апреля 2015

Каждый раз, когда объект создается в нем, он попадает в область памяти, известную как куча. Примитивные переменные, такие как int и double размещаются в стеке, если они являются локальными переменными метода, и в куче, если они являются членами переменные В методах локальные переменные помещаются в стек при вызове метода и указатель стека уменьшается, когда вызов метода завершен. В многопоточном приложении каждый поток будет иметь свой собственный стек, но будет иметь ту же кучу. Вот почему в вашем коде следует соблюдать осторожность, чтобы избежать проблемы одновременного доступа в пространстве кучи. Стек является потокобезопасным (каждый поток будет иметь свой собственный стек), но куча не является потокобезопасной, если не защищена синхронизацией через ваш код.

Эта ссылка также полезна http://www.programmerinterview.com/index.php/data-structures/difference-between-stack-and-heap/

1 голос
/ 20 декабря 2010

простых мер

Тип значения может быть размещен в STACK, это детали реализации, которые могут быть выделены для некоторой футуристической структуры данных.

Итак, лучше понять, как работает значение и ссылочный тип. Тип значения будет скопирован по значению, что означает, что когда вы передаете тип значения в качестве параметра в FUNCTION, чем он будет скопирован по своей природе, это означает Всего новая копия.

Типы ссылок передаются по ссылке (даже если учесть, что ссылка не будет снова сохранять адрес в некоторых будущих версиях, он может быть сохранен в некоторых других структурах данных.)

так в вашем случае

myInt - это int, который инкапсулируется в классе, который выводит ссылочный тип, поэтому он будет привязан к экземпляру класса, который будет храниться в «THE HEAP».

Я бы сказал, вы можете начать читать блоги, написанные Эриком Липпертом.

Блог Эрика

0 голосов
/ 30 марта 2018

m - это ссылка на объект MyClass, поэтому m хранится в стеке основного потока, а объект MyClass хранится в куче.Поэтому myInt и myString хранят в куче.Обратите внимание, что m является только ссылкой (адресом памяти) и находится в главном стеке.когда m освобожден, GC удаляет объект MyClass из кучи. Более подробно читайте все четыре части этой статьи https://www.c -sharpcorner.com / article / C-Sharp-heaping-vs-stacking-in-net-part-i /

...