Использование, “новое” на структуре, выделяют его на "куче" или стеке?

281
задан ryeguy 21 January 2014 в 17:43
поделиться

7 ответов

Хорошо, давайте посмотрим, могу ли я сделать это немного более ясным.

Во-первых, Пепел является правильным: вопрос не о том, где переменные типа значения выделяются. Это - различный вопрос - и один, к которому ответ не просто "на стеке". Это более сложно, чем это (и сделанный еще более осложненным C# 2). Я имею статья о теме и подробно остановлюсь на нем, если требуется, но давайте иметь дело только с new оператор.

, Во-вторых, все это действительно зависит, на каком уровне Вы говорите о. Я смотрю на то, что компилятор делает с исходным кодом, с точки зрения IL, который это создает. Более, чем возможно, что JIT-компилятор сделает умные вещи с точки зрения оптимизации далеко довольно большого "логического" выделения.

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

Наконец, все это только с текущей реализацией. Спецификация C# не определяет большую часть этого - это - эффективно деталь реализации. Существуют те, кто полагает, что разработчики управляемого кода действительно не должны заботиться. Я не уверен, что пошел бы, что далеко, но стоит вообразить мир, где на самом деле все локальные переменные живут на "куче" - который все еще соответствовал бы спецификации.

<час>

существует две различных ситуации с new оператор на типах значения: можно или вызвать конструктора без параметров (например, new Guid()) или parameterful конструктора (например, new Guid(someString)). Они генерируют существенно отличающийся IL. Для понимания, почему, необходимо сравнить C# и спецификации CLI: согласно C#, все типы значения имеют конструктора без параметров. Согласно спецификации CLI, никакой типы значения имеют конструкторов без параметров. (Выберите конструкторов типа значения с отражением некоторое время - Вы не найдете без параметров.)

имеет смысл для C# рассматривать, "инициализируют значение с, обнуляет" как конструктор, потому что это сохраняет язык последовательным - можно думать new(...) как [1 126] всегда вызов конструктора. Имеет смысл для CLI думать о нем по-другому, поскольку нет никакого реального кода для вызова - и конечно никакой определенный для типа код.

Это также имеет значение, что Вы собираетесь сделать со значением после инициализации его. IL, используемый для [1 140]

Guid localVariable = new Guid(someString);

, отличается от IL, используемого для:

myInstanceOrStaticVariable = new Guid(someString);

, Кроме того, если значение используется в качестве промежуточного значения, например, аргумента вызову метода, вещи немного отличаются снова. Для показа всех этих различий вот, короткая тестовая программа. Это не показывает различие между статическими переменными и переменными экземпляра: IL отличался бы между [1 110] и stsfld, но это - все.

using System;

public class Test
{
    static Guid field;

    static void Main() {}
    static void MethodTakingGuid(Guid guid) {}


    static void ParameterisedCtorAssignToField()
    {
        field = new Guid("");
    }

    static void ParameterisedCtorAssignToLocal()
    {
        Guid local = new Guid("");
        // Force the value to be used
        local.ToString();
    }

    static void ParameterisedCtorCallMethod()
    {
        MethodTakingGuid(new Guid(""));
    }

    static void ParameterlessCtorAssignToField()
    {
        field = new Guid();
    }

    static void ParameterlessCtorAssignToLocal()
    {
        Guid local = new Guid();
        // Force the value to be used
        local.ToString();
    }

    static void ParameterlessCtorCallMethod()
    {
        MethodTakingGuid(new Guid());
    }
}

Вот IL для класса, исключая несоответствующие биты (такие как nops):

.class public auto ansi beforefieldinit Test extends [mscorlib]System.Object    
{
    // Removed Test's constructor, Main, and MethodTakingGuid.

    .method private hidebysig static void ParameterisedCtorAssignToField() cil managed
    {
        .maxstack 8
        L_0001: ldstr ""
        L_0006: newobj instance void [mscorlib]System.Guid::.ctor(string)
        L_000b: stsfld valuetype [mscorlib]System.Guid Test::field
        L_0010: ret     
    }

    .method private hidebysig static void ParameterisedCtorAssignToLocal() cil managed
    {
        .maxstack 2
        .locals init ([0] valuetype [mscorlib]System.Guid guid)    
        L_0001: ldloca.s guid    
        L_0003: ldstr ""    
        L_0008: call instance void [mscorlib]System.Guid::.ctor(string)    
        // Removed ToString() call
        L_001c: ret
    }

    .method private hidebysig static void ParameterisedCtorCallMethod() cil  managed    
    {   
        .maxstack 8
        L_0001: ldstr ""
        L_0006: newobj instance void [mscorlib]System.Guid::.ctor(string)
        L_000b: call void Test::MethodTakingGuid(valuetype [mscorlib]System.Guid)
        L_0011: ret     
    }

    .method private hidebysig static void ParameterlessCtorAssignToField() cil managed
    {
        .maxstack 8
        L_0001: ldsflda valuetype [mscorlib]System.Guid Test::field
        L_0006: initobj [mscorlib]System.Guid
        L_000c: ret 
    }

    .method private hidebysig static void ParameterlessCtorAssignToLocal() cil managed
    {
        .maxstack 1
        .locals init ([0] valuetype [mscorlib]System.Guid guid)
        L_0001: ldloca.s guid
        L_0003: initobj [mscorlib]System.Guid
        // Removed ToString() call
        L_0017: ret 
    }

    .method private hidebysig static void ParameterlessCtorCallMethod() cil managed
    {
        .maxstack 1
        .locals init ([0] valuetype [mscorlib]System.Guid guid)    
        L_0001: ldloca.s guid
        L_0003: initobj [mscorlib]System.Guid
        L_0009: ldloc.0 
        L_000a: call void Test::MethodTakingGuid(valuetype [mscorlib]System.Guid)
        L_0010: ret 
    }

    .field private static valuetype [mscorlib]System.Guid field
}

, Как Вы видите, существует много различных инструкций, используемых для вызова конструктора:

  • newobj: Выделяет значение на стеке, вызывает параметризованного конструктора. Используемый для промежуточных значений, например, для присвоения на поле или использование в качестве аргумента метода.
  • call instance: Использует уже выделенное место хранения (ли на стеке или не). Это используется в коде выше для присвоения локальной переменной. Если той же локальной переменной присваивают значение несколько раз с помощью нескольких new вызовы, она просто инициализирует данные поверх старого значения - она не делает , выделяют больше стекового пространства каждый раз.
  • initobj: Использует уже выделенное место хранения и просто вытирает данные. Это используется для всех наших вызовов конструктора без параметров, включая тех, которые присваивают локальной переменной. Для вызова метода промежуточная локальная переменная эффективно представлена, и ее значение, вытертое [1 116].

я надеюсь, что это показывает, насколько сложный тема при проливании небольшого количества света на него одновременно. В [1 128] [приблизительно 1 128] концептуальные смыслы, каждый вызов к [1 117] выделяет место на стеке - но как мы видели, это не то, что действительно происходит даже на уровне IL. Я хотел бы выделить один особый случай. Возьмите этот метод:

void HowManyStackAllocations()
{
    Guid guid = new Guid();
    // [...] Use guid
    guid = new Guid(someBytes);
    // [...] Use guid
    guid = new Guid(someString);
    // [...] Use guid
}

, Который "логически" имеет 4 выделения стека - один для переменной, и один для каждого из три new вызовы - но на самом деле (для того определенного кода) стек только выделяется однажды, и затем то же место хранения снова используется.

РЕДАКТИРОВАНИЕ: Только, чтобы быть ясным, это только верно в некоторых случаях..., в частности, значение [1 119] не будет видимо, если Guid конструктор выдаст исключение, которое является, почему компилятор C# в состоянии снова использовать тот же слот стека. Посмотрите Eric Lippert сообщение в блоге на конструкции типа значения для получения дополнительной информации и случая, где это не делает , применяются.

я изучил много в записи этого ответа - попросите разъяснение, если какой-либо из него неясен!

300
ответ дан Jon Skeet 23 November 2019 в 01:59
поделиться

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

, Если структура выделяется на "куче", то вызов нового оператора не на самом деле необходим для выделения памяти. Единственная цель состояла бы в том, чтобы установить значения полей согласно тому, что находится в конструкторе. Если конструктора не вызовут, то все поля получат свои значения по умолчанию (0 или пустой указатель).

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

38
ответ дан Jeffrey L Whitledge 23 November 2019 в 01:59
поделиться

Помещать его сжато, новый - неправильное употребление для структур, называть новым просто вызывает конструктора. Единственное место хранения для структуры является местоположением, это определяется.

, Если это - членская переменная, это хранится непосредственно в том, в чем это определяется, если это - локальная переменная или параметр, это хранится на стеке.

Контраст это к классам, которые имеют ссылку везде, где структура была бы сохранена в целом, в то время как контрольные точки где-нибудь на "куче". (Участник в, локальный / параметр на стеке)

Это может помочь немного изучить C++, где нет реального различия между классом/структурой. (На языке существуют аналогичные имена, но они только обращаются к доступности по умолчанию вещей), Когда Вы называете новыми, Вы получаете указатель на местоположение "кучи", в то время как, если у Вас есть неуказательная ссылка, это хранится непосредственно на стеке или в другом объекте, структурах крыла в C#.

12
ответ дан Guvante 23 November 2019 в 01:59
поделиться

Как со всеми типами значения, всегда идут структуры, где они были , объявил .

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

Редактирование: у меня был mistankely, ответил, что они ВСЕГДА входят в стек. Это неправильно .

5
ответ дан Community 23 November 2019 в 01:59
поделиться

Я, вероятно, пропускаю что-то здесь, но почему мы заботимся о выделении?

типы Значения передаются значением;) и таким образом не может быть видоизменен в различном объеме, чем, где они определяются. Чтобы быть в состоянии видоизменить значение, необходимо добавить [касательно] ключевого слова.

Ссылочные типы передаются ссылкой и могут быть видоизменены.

существуют, конечно, неизменные строки ссылочных типов, являющиеся самой популярной.

расположение/инициализация Массива: типы Значения-> обнуляют память [имя, zip] [имя, zip] Ссылочные типы-> нулевая память-> пустой указатель [касательно] [касательно]

4
ответ дан user18579 23 November 2019 в 01:59
поделиться

В значительной степени структуры, которые считают типами Значения, выделяются на стеке, в то время как объекты выделяются на "куче", в то время как ссылка на объект (указатель) выделяется на стеке.

1
ответ дан bashmohandes 23 November 2019 в 01:59
поделиться

Структуры выделяются стеку. Вот полезное объяснение:

Структуры

Кроме того, классы, когда инстанцировано в.NET выделяют память на "куче" или зарезервированном пространстве памяти.NET. Принимая во внимание, что структуры приводят к большей эффективности, когда инстанцировано из-за выделения на стеке. Кроме того, нужно отметить, что передающие параметры в структурах сделаны так значением.

1
ответ дан Dave 23 November 2019 в 01:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: