Как управляемая память .net обрабатывает типы значений внутри объектов? - PullRequest
5 голосов
/ 24 августа 2008
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. Есть ли подробные, но понятные ресурсы по этой теме?

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

Edit:

Рекомендуемое чтение по этой теме:

  1. CLR Via C # Джеффри Рихтера
  2. Essential .NET от Don Box

Ответы [ 6 ]

9 голосов
/ 24 августа 2008

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

Размер объекта класса в управляемой куче является суммой его полей типа значения, указателей ссылочного типа и дополнительных служебных переменных 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 , чтобы напрямую скопировать значение из этого расположения управляемой кучи в стек потока. С этими типами операций не происходит коробка / распаковка.

2 голосов
/ 24 августа 2008
  • Ответ № 1: Куча. Перефразируя Дона Бокса из его превосходного «Essential .Net Vol 1»

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

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

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

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

2 голосов
/ 24 августа 2008

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

2 голосов
/ 24 августа 2008
  1. Любые ссылки или типы значений, которыми владеет объект, живут в куче.
  2. Только если вы разыгрываете целые объекты на объекты.
1 голос
/ 24 августа 2008

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

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

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

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

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

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

0 голосов
/ 28 января 2014

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

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, кроме доступа к его полям, но это заняло бы место, независимо от того, использовалось ли оно вообще когда-либо].

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...