Обновить
Комментарии 26
>> существует 2 вида объектов: value types и reference types,

А вот unsafe pointer — он value-type или reference-type? Он ведь не обычный указатель, по типу того, что используется в Си, так, и не обычная объектная ссылка? Так сколько он байт занимает?
вообще это уже третий тип в .NET — pointer type. представляет он собой DWORD-адрес на конкретный экземпляр либого value type + bool.
главное предназначение — избежать копирования value type объекта пр присваивании переменной. еще одна роль — вызов нативных функций.
если говорить про DWORD — то 4 байта x86, 8 байт x64.
а уже размеры каждого value type определены в CTS.
>> Прежде вспомним, что в .NET существует 2 вида объектов: value types и reference types, которые создаются, соответственно, в стеке и куче (управляемом сборщиком мусора).

Чушь. За такое надо бить по рукам. Вы утверждаете что массив интов лежит в стеке? Или что локальная интовая переменная не может лежать в регистре (который, на минуточку не стек и не куча). Value и reference вообще никак не связаны с местом расположения переменной. Локальная переменная может лежать где угодно: в регистре, стеке и куче. Это зависит от ожидаемой продолжительности жизни — если локальная переменная участвует в лямбде то она благополучно переживет свой изначальный метод в куче.

На всякий случай читайте первоисточники
НЛО прилетело и опубликовало эту надпись здесь
Экземпляры объектов ссылочного типа хранятся в управляемой куче всегда; экземпляры объектов типа значений — в стеке управляемого потока среды .NET, за исключением случаев, когда их время жизни должно быть продлено (объект типа значения является полем класса, элементом массива, локальной переменной в блоке итератора или локальной переменной, замкнутой в лямбде/анонимном методе). Интересно что язык C++/CLI позволяет создать массив значений в стеке.

Вывод: автор не так уж и неправ. Reference types хранятся в куче, value types обычно хранятся в стеке. Многие авторы, включая Рихтера, связывают местоположение переменной и её тип таким образом, потому как в большинстве случаев нет необходимости погружаться в низкоуровневые подробности, особенно в подробности реализации компилятора. .NET-программист может ограничится частично верным определением, если только не пишет серьёзно много неуправляемого кода (что, впрочем, и утверждается в рекомендованной статье).
Во-первых, reference создается и живет только в куче, причем не только в GC Heap, но может и в Large Object Heap и т.д.
Во-вторых, время жизни value type определяется declaration scope.
В-третьих, для увеличения продолжительности жизни экземпляра value type, можно и без анонимных методов, и без лямбда-выражений обойтись — использовать boxing (CLR заворачивает значение переменной и помещает в GC Heap).
Возможность нахождения value type объекта в регистре зависит от платформы (так существуют отличия между desktop и server CLR не только в плане сборщика мусора), еще это зависит от самого JIT. Так Windows (и .NET соответственно) запускаются не только на x86, x64, но также еще существует ARM-версия — Windows Embedded. А еще есть .NET MicroFramework с TinyCLR.
Иногда из-за трудности определения возможной жизни объекта, CLR может поместить value type в кучу, для обеспечения максимальной жизни.
И, наконец, это зависит от использование нативного кода.

Как видим, это все детали имплементации конкретной CLR + языковые возможности и требования, например C#
Правильно ли я понимаю, что если запросить по адресу размер некоторого класса А, который имеет поля классов В, С..., то SOS вернет суммарный размер класса А, B, C? Или вернет размер класса A + размер ссылок на классы B и С?
НЛО прилетело и опубликовало эту надпись здесь
Предположим что он возвращает 1) а не 2). Тогда что должен вернуть sos для класса, который в одном из полей содержит ссылку на самого себя? +inf?
Делаем вывод…
если одно из полей экземпляра класса содержит ссылку на самого себя, то при !objsize будет возвращен удвоенный размер экземпляра + размер строки (в нашем случае 180 байт) без рекурсии.
!do производит инспекцию только по полям и структуре классов, без подсчета размеров др. reference types объектов.
SOS вернет суммарный размер класса А, B, C, если использовать !objsize.
Если !do, то без размеров B, C.
Спасибо =) Делаю вывод, что SOS исключительных ситуациях действительно будет необходим
меня терзают смутные сомнения (с)
размер decimal 16 байт.
прежде чем писать, может пробовали хотя бы посмотреть MSDN?
если да, то
1) decimal есть не что иное как 128-битовое число, НО
2) размер колеблется от ±1.0 ✕ 10^28 до ±7.9 ✕ 10^28
3) Содержит 128-битовые (16-байтовые) значения со знаком, представляющие 96-битовые (12-байтовые) целые числа, масштабируемые с переменной степенью 10.

еще можно было просто проверить через !objsize.
и еще посмотреть на расчеты в статье.
и, да, оператор sizeof врет, возвращая размер 16.
А почему вы не согласны, что decimal занимает 128 бит (16 байт), если он реализован согласно IEEE 754-2008 как decimal128?

[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct X
{
public decimal f1;
public decimal f2;
public decimal f3;
public decimal f4;
}


!do 02532e2c
Name: Test.Program+X
MethodTable: 002a3094
EEClass: 002a135c
Size: 72(0x48) bytes
(c:\Projects\other\ClassLibrary1\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe)
Fields:
MT Field Offset Type VT Attr Value Name
717e7f08 4000001 4 System.Decimal 1 instance 02532e30 f1
717e7f08 4000002 14 System.Decimal 1 instance 02532e40 f2
717e7f08 4000003 24 System.Decimal 1 instance 02532e50 f3
717e7f08 4000004 34 System.Decimal 1 instance 02532e60 f4


Каждый Decimal как видно занимает ровно 0x10 байт.

Посмотрим отдельно на decimal — состоит из 4х int:
Name: System.Decimal
...
Fields:
MT Field Offset Type VT Attr Value Name
71812dbc 400037e 4 System.Int32 1 instance 0 flags
71812dbc 400037f 8 System.Int32 1 instance 0 hi
71812dbc 4000380 c System.Int32 1 instance 0 lo
71812dbc 4000381 10 System.Int32 1 instance 0 mid
...


Почему врет по вашему sizeof и сколько он должен возвращать?
Приношу свои извинения. Действительно Decimal составляет 16 байт.
Именно понятие 96-битовых чисел у меня и запечатлелось как норма, что есть неправда.
>Reference type pointer — ссылка на объект, хранящаяся в переменной, размещенной в стеке со смещением 4.

несколько раз перечитал, не понял, что это
и почему его нет в общей сумме?
смотрите, в классе
class MyExampleClass
{
  string StringValue = "String";  // 4 байта
}

StringValue представляет собой ссылку размером 4 байта. размер самого экземпляра класса System.String будет равен 26 байт.
привожу цитату из статьи:
В-третьих, System.String – ссылочный тип, а это значит, что располагается он в GC Heap, и будет состоять из SyncBlock, TypeHandle, Reference point + остальные поля класса. Reference point здесь браться в расчет не будет, т.к. уже посчитан в самом классе MyExampleClass (ссылка 4 байта).
а если в моем классе будет 10 строк, то по вашей логике должно быть 10 штук Reference type pointer на схеме?

ссылочные объекты занимают 4 байта (на машине соответствующей разрядности) и вполне попадают в Object fields. незачем пихать в схему лишнюю сущность
тот же Рихтер рисует только Type object ptr и Sync index: www.rvenables.com/linkjackandsufferaccidentaldroptable/clr_via_csharp_f4.9.png

>>Однако в SyncBlock может хранится хеш-код объекта (при вызове метода GetHashCode), или номер записи syncblk
«все смешалось в доме Облонских»
все совсем наоборот. для System.Object в CLR 1-2 в качестве хешкода использовался Sync index (поэтому вызов GetHashCode() на нем приводил к созданию Sync block'a)

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

на ошибку с привязкой value types к стеку вам уже указали
а если в моем классе будет 10 строк, то по вашей логике должно быть 10 штук Reference type pointer на схеме?

ссылочные объекты занимают 4 байта (на машине соответствующей разрядности) и вполне попадают в Object fields. незачем пихать в схему лишнюю сущность

именно поля класса я имел и ввиду, когда говорил о том, что
Reference point здесь браться в расчет не будет, т.к. уже посчитан в самом классе MyExampleClass (ссылка 4 байта).

если картинка непонятна, то это другое дело.
«все смешалось в доме Облонских»
все совсем наоборот. для System.Object в CLR 1-2 в качестве хешкода использовался Sync index (поэтому вызов GetHashCode() на нем приводил к созданию Sync block'a)

не могу понять что в этих строках непонятного:
Первоначально значение SyncBlock равно нулю. Однако в SyncBlock может хранится хеш-код объекта (при вызове метода GetHashCode), или номер записи syncblk, который помещает в заголовок объекта среда при синхронизации (использование lock, либо напрямую Monitor.Enter).


и, наконец, насчет
подсчет длины строки также выполнен неверно. m_firstChar является первым элементом в последовательности символов, и поэтому присутствует один раз, а вы посчитали его дважды

вообще-то посчитано правильно. m_firstChar является отдельным полем класса + не является shared.
более того, т.к. оно еще и value type, тогда при присваивании ей значения char копируется из массива символов. поправьте меня, если я ошибаюсь.

на ошибку с привязкой value types к стеку вам уже указали

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

картинка просто не соотвествует действительности

>Первоначально значение SyncBlock равно нулю. Однако в SyncBlock может хранится хеш-код объекта (при вызове метода GetHashCode), или номер записи syncblk,

вы путаете причину и следствие. не «SyncBlock может хранить хеш-код», а «Sync Index может использоваться в качестве хеш-кода»

>это не является ошибкой. см. мой коммент выше.

в той формулировке, как это сделано в статье, именно что ошибка. а вашим комментариям, к сожалению, доверять нельзя
почиайте Липперта что ли:
blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx
blogs.msdn.com/b/ericlippert/archive/2009/04/27/the-stack-is-an-implementation-detail.aspx

>вообще-то посчитано правильно. m_firstChar является отдельным полем класса
>тогда при присваивании ей значения char копируется из массива символов.

возьмите отладчик и посмотрите побайтно, что в строке есть и чего там нет

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

P.S.
Эрика Липперта я читаю, и не только его ;)
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.