Как управляемая память .net обрабатывает типы значений внутри объектов?

public class MyClass
{
    public int Age;
    public int ID;
}

public void MyMethod() 
{
    MyClass m = new MyClass();
    int newID;
}

Насколько я понимаю, верно следующее:

  1. Ссылка m живет в стеке и выходит из области видимости при выходе из MyMethod ().
  2. Тип значения newID живет в стеке и выходит из области видимости при выходе из MyMethod ().
  3. Объект, созданный новым оператором, живет в куче и становится доступным для восстановления GC при выходе из MyMethod (), предполагая, что никакой другой ссылки на объект не существует.

Вот мой вопрос:

  1. Существуют ли типы значений внутри объектов в стеке или в куче?
  2. Стоит ли беспокоиться о типах упаковки или распаковки в объекте?
  3. Есть ли подробные, но понятные ресурсы по этой теме?

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

Редактировать:

Предлагаемое чтение по этой теме:

  1. CLR Via C # Джеффри Рихтер
  2. Essential .NET от Дона Бокса
24.08.2008 03:37:47
6 ОТВЕТОВ
РЕШЕНИЕ

Значения типа значения для класса должны находиться вместе с экземпляром объекта в управляемой куче. Стек потока для метода живет только в течение срока действия метода; как значение может сохраняться, если оно существует только в этом стеке?

Размер объекта класса в управляемой куче - это сумма его полей типа значения, указателей ссылочного типа и дополнительных служебных переменных CLR, таких как индекс блока Sync. Когда кто-либо присваивает значение полю типа значения объекта, CLR копирует это значение в пространство, выделенное в объекте для этого конкретного поля.

Взять, к примеру, простой класс с одним полем.

public class EmbeddedValues
{
  public int NumberField;
}

И с этим, простой класс тестирования.

public class EmbeddedTest
{
  public void TestEmbeddedValues()
  {
    EmbeddedValues valueContainer = new EmbeddedValues();

    valueContainer.NumberField = 20;
    int publicField = valueContainer.NumberField;
  }
}

Если вы используете дизассемблер MSIL, предоставляемый .NET Framework SDK, для просмотра кода IL для EmbeddedTest.TestEmbeddedValues ​​()

.method public hidebysig instance void  TestEmbeddedValues() cil managed
{
  // Code size       23 (0x17)
  .maxstack  2
  .locals init ([0] class soapextensions.EmbeddedValues valueContainer,
           [1] int32 publicField)
  IL_0000:  nop
  IL_0001:  newobj     instance void soapextensions.EmbeddedValues::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldc.i4.s   20
  IL_000a:  stfld      int32 soapextensions.EmbeddedValues::NumberField
  IL_000f:  ldloc.0
  IL_0010:  ldfld      int32 soapextensions.EmbeddedValues::NumberField
  IL_0015:  stloc.1
  IL_0016:  ret
} // end of method EmbeddedTest::TestEmbeddedValues

Обратите внимание, что CLR сообщается, чтобы stfld загруженное значение «20» в стеке помещалось в расположение поля NumberField загруженного EmbeddValues ​​непосредственно в управляемую кучу. Аналогично, при получении значения он использует инструкцию ldfld для непосредственного копирования значения из этого расположения управляемой кучи в стек потока. С этими типами операций не происходит коробка / распаковка.

9
10.11.2011 02:22:30
  1. Любые ссылки или типы значений, которыми владеет объект, живут в куче.
  2. Только если вы применяете целые объекты к объектам.
2
24.08.2008 03:52:14

Лучший ресурс, который я видел для этого, - книга Джеффри Рихтера «CLR via C #». Это стоит прочитать, если вы занимаетесь разработкой .NET. Основываясь на этом тексте, я понимаю, что типы значений в ссылочном типе живут в куче, встроенной в родительский объект. Типы ссылок всегда в куче. Бокс и распаковка не симметричны. Бокс может быть более серьезной проблемой, чем распаковка. Бокс будеттребуется скопировать содержимое типа значения из стека в кучу. В зависимости от того, как часто это происходит с вами, может быть бессмысленно иметь структуру вместо класса. Если у вас есть некоторый критичный к производительности код, и вы не уверены, что происходит упаковка и распаковка, используйте инструмент для проверки кода IL вашего метода. Вы увидите окно слов и распаковать в IL. Лично я бы измерил производительность своего кода и только потом посмотрел, не является ли это источником беспокойства. В вашем случае я не думаю, что это будет такой критической проблемой. Вам не нужно копировать из стека в кучу (блок) каждый раз, когда вы получаете доступ к этому типу значения внутри ссылочного типа. В этом сценарии бокс становится более значимой проблемой.

2
24.08.2008 05:06:59

Существуют ли типы значений внутри объектов в стеке или в куче?

В кучу. Они являются частью распределения отпечатка объекта, как указатели для хранения ссылок.

Стоит ли беспокоиться о типах упаковки или распаковки в объекте?

Здесь нет бокса.

Есть ли подробные, но понятные ресурсы по этой теме?

+1 голос за книгу Рихтера.

1
24.08.2008 05:55:30
  • Ответ № 1: куча. Перефразируя Дона Бокса из его превосходного «Essential .Net Vol 1»

Ссылочные типы (RT) всегда дают экземпляры, которые размещены в куче. Напротив, типы значений (VT) зависят от контекста. Если локальная переменная является VT, CLR выделяет память в стеке. Если поле в классе является членом VT, то CLR выделяет память для экземпляра как часть макета объекта / Типа, в котором объявлено поле.

  • Ответ № 2: Нет. Бокс будет происходить только при доступе к структуре через указатель объекта / интерфейсный указатель. obInstance.VT_typedfield не будет боксировать.

    Переменные RT содержат адрес объекта, на который он ссылается. 2 RT var может указывать на один и тот же объект. Напротив, переменные VT сами являются экземплярами. 2 VT var не может указывать на один и тот же объект (структура)

  • Ответ № 3: Essential .net Дон Бокса / CLR Джеффри Рихтера через C #. У меня есть копия первого ... хотя позднее может быть более обновленным для ревизий .Net

2
24.08.2008 07:43:38

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

struct Foo {public int x,y; int z;}

заявление Foo bar;вызовет bar.x, bar.yи bar.zхраниться там , где barбудет храниться. Добавление такого объявления barв класс с точки зрения макета хранилища будет эквивалентно добавлению трех intполей. Действительно, если один никогда ничего не делал с barисключением доступа к своим полям, поля barбудут вести себя так же , как было бы три поля bar_x, bar_yи bar_cantaccessthis_z[доступ к последним можно было бы потребовать делать вещи с barдругой , чем доступ к его полям, но это заняло бы место ли или нет, это когда-либо использовалось для чего-либо].

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

0
27.01.2014 20:20:46