Как будет выглядеть память для этого объекта?

Мне интересно, как будет выглядеть макет памяти для этого класса (его объекта):

class MyClass
{
    string myString;

    int myInt;

    public MyClass(string str, int i)
    {
        myString = str;
        myInt = i;
    }
}

MyClass obj = new MyClass("hello", 42);

Может ли кто-нибудь это представить?

Обновление:

Основываясь на ответе Оливье Рожье и комментариях ckuri и Jon Skeet, я попытался составить диаграмму высокого уровня, на которую сильно повлиял статья в блоге разработчиков, упомянутая ckuri.

Итак, в моем понимании:

  1. obj (ссылка 8 байт) указывает на объект, включая метаданные (на самом деле не на его начало, но для простоты проигнорируем это).

  2. В этом месте хранится myInt и опорное значение myString (которое является ссылкой на реальное строковое значение)

введите здесь описание изображения

Я не хочу вдаваться в последние подробности, но мне все же любопытно:

  1. Если необходимо получить доступ к obj.myString, необходимы ли два «поиска», например. сначала ищем obj, затем следуем за ним и ищем myString, или есть что-то вроде глобальной таблицы адресов, где непосредственно хранится адрес для obj.myString?

  2. Где хранится эталонное значение obj? Является ли он частью блока объектов program, как myString является частью блока объектов obj? (при условии, что obj создается внутри экземпляра program)


person stefan.at.wpf    schedule 14.10.2019    source источник
comment
Я очень смущен тем, что вы спрашиваете   -  person maccettura    schedule 14.10.2019
comment
Возможный дубликат stackoverflow.com/questions/8951828/clr-class-memory -макет   -  person Jay Buckman    schedule 14.10.2019
comment
Что означает «должен быть доступен» в вашем втором вопросе под номером (1)? Можете привести пример доступа? Также я не понимаю, что вы подразумеваете под глобальной таблицей адресов.   -  person Eric Lippert    schedule 15.10.2019
comment
Также было бы полезно понять, с какой целью вы задаете эти вопросы; подавляющему большинству разработчиков C# никогда не приходится беспокоиться об этом. Есть ли какая-то более глубокая проблема, которую вы пытаетесь здесь решить? Если да, скажите, что это за проблема, и мы поможем вам решить ее напрямую.   -  person Eric Lippert    schedule 15.10.2019
comment
Кроме того, ваша диаграмма неправильно показывает структуру строкового объекта, которая значительно сложнее, чем вы показали здесь; тебя это волнует?   -  person Eric Lippert    schedule 15.10.2019
comment
@EricLippert: я исправил визуализацию, теперь строка стоит перед int (как в исходном коде). Проверим ваш ответ. также было бы интересно, как выглядит строковый объект. никакой особой проблемы я не пытаюсь решить, просто учусь и любопытствую.   -  person stefan.at.wpf    schedule 16.10.2019
comment
Мэтт Уоррен — MVP, а не архитектор компилятора C# — написал хороший пост, в котором рассказывается об основах компоновки строк. mattwarren.org/2016/05/31 /. Если вам нужна историческая перспектива происхождения строк с префиксом длины в инструментах разработчика Microsoft, см. мою статью 2003 года на эту тему: ericlippert.com/2003/09/12/   -  person Eric Lippert    schedule 16.10.2019


Ответы (2)


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

Во-первых, вам нужно 4 байта на x32 или 8 байт на x64, чтобы хранить ссылку на адрес памяти объекта (ссылка — это скрытый указатель, чтобы забыть об управлении).

Далее, у объекта есть два члена данных:

  • Одно целое, занимающее 4 байта.
  • Одна строка, которая здесь занимает 5 символов: 5x2 байта = 10 байт.

Таким образом, для данных объект занимает 18 байт в системе x32 или 22 байта в системе x64.

Поскольку строковый объект содержит целое число для длины, размер немного больше: 22 на x32 и 26 на x64.

Поскольку строка является ссылкой, нам нужно снова добавить 4 или 8 байтов => 26 или 34 байта.

Поскольку строка имеет некоторые другие статические поля и поля экземпляра в объявлении класса, такие как первый char, требуется немного больше, чем это.

Является ли строка на самом деле массивом символов или у нее просто есть индексатор?

Кроме того, в памяти в сегменте кода находятся инструкции кода методов. Этот код общий для всех экземпляров.

Кроме того, существуют таблицы классов и виртуальные таблицы для описания типов, сигнатур методов и правил полиморфизма.

Если объект создается в методе, он использует динамическую память.

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

А память подобна поезду, где вагоны — это байты.

Вот псевдосхема памяти.

Это не совсем истинная реальность, но она может помочь понять:

введите здесь описание изображения

При доступе к переменной в классе C# весь класс считывается из памяти?

C# Heap(ing) Сравнение со стекированием в .NET

Байт — это элементарная единица памяти, в которой одновременно хранится одно значение в диапазоне от 0 до 255 (без знака) или от -128 до +127 (со знаком).

Изучите основы переменных типов данных C#.

Сдвиг поведения для целых чисел со знаком

Учебник по представлению данных


Увидев этот набросок сегодня (2021.01.28), я понимаю, что он может вводить в заблуждение, и именно поэтому я написал Это не совсем истинная реальность, но может помочь понять, потому что на самом деле код реализация методов загружается из бинарных файлов EXE и DLL при запуске процесса и сохраняется в СЕГМЕНТЕ КОДА, так как все данные, статические (литералы) и динамические (экземпляры) находятся в СЕГМЕНТЕ ДАННЫХ (если ничего не изменилось со времен x32 и защищенного режима). Не виртуальные таблицы методов, а также виртуальные таблицы методов не хранятся в сегменте данных для каждого экземпляра объектов. Я не помню подробностей, но эти таблицы для кода. Также данные каждого экземпляра объекта являются проекцией его определения, а также его предков, в одном месте, один полный экземпляр.

Сегментация памяти

сегментация памяти x86

person Olivier Rogier    schedule 14.10.2019
comment
Строки не имеют ссылок на массивы символов — текстовые данные находятся непосредственно внутри строкового объекта. - person Jon Skeet; 14.10.2019
comment
@JonSkeet, ckuri, Оливье Рожье: попробовал визуализацию высокого уровня, добавленную к моему исходному вопросу. Можете ли вы, ребята, проверить это и, возможно, также взглянуть на два новых вопроса? :-) - person stefan.at.wpf; 15.10.2019
comment
@OlivierRogier спасибо! принято как ответ из-за всей этой работы и большого количества контента, дополнительный бонус за визуальные эффекты ;-) - person stefan.at.wpf; 16.10.2019

В этом месте хранится myInt и значение ссылки myString (которое является ссылкой на реальное строковое значение)

Давайте удостоверимся, что вы не идете по плохим путям здесь.

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

Во-вторых, неясно, что вы подразумеваете под «настоящим строковым значением». Строки имеют ссылочный тип. Настоящим значением строки является ссылка. Значения содержимого строки находятся в указанной позиции.

если необходимо получить доступ к obj.myString, необходимы ли два «поиска», например. сначала ищем obj, затем следуем за ним и ищем myString

Я предполагаю, что под "поиском" вы подразумеваете разыменование.

Так, например, если у нас есть:

var obj = whatever;
char c = obj.myString[1];

тогда да, у нас есть два разыменования. . разыменовывает obj, чтобы получить myString, который является ссылкой. [1] разыменовывает myString, чтобы получить char.

Где хранится эталонное значение obj?

obj — это переменная. Переменная — это место хранения. Это место хранения может быть в нескольких местах:

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

  • Если известно, что obj недолговечен, он помещается в долгосрочный пул, также известный как управляемая куча.

person Eric Lippert    schedule 14.10.2019
comment
Что такое эфемерный объект? Имеет ли это какое-либо отношение к Эфемерные поколения и сегменты? - person Luca Cremonesi; 15.10.2019
comment
@LucaCremonesi: Под эфемерностью я подразумеваю в этом контексте следующее: рассмотрим фрагмент тела метода, такой как int x = Foo(); int y = x + Bar(); Blah(y);, где x и y больше нигде в теле не используются. Компилятор сгенерирует код для создания кадра стека для активации метода; сколько слотов он должен зарезервировать в верхней части кадра для местных жителей? Похоже, что двух целых чисел достаточно, но компилятор может решить, что эта программа такая же, как Blah(Foo() + Bar()), и сгенерировать ноль зарезервированных слотов. - person Eric Lippert; 15.10.2019
comment
@LucaCremonesi: переменные x и y в этом случае могут стать эфемерными. Их хранилище существует только пока переменная используется, потому что хранилище просто помещается в стек оценки (в IL), когда это необходимо. Затем джиттер превратит его либо в толчок стека, либо в выделение регистра, как он считает нужным, и кадр стека станет немного меньше. Это небольшая оптимизация, но она складывается. Однако это может усложнить отладку программ и сократить время жизни, чем вы ожидаете, поэтому компилятор не всегда принимает эту оптимизацию. - person Eric Lippert; 15.10.2019
comment
@LucaCremonesi: К сожалению, команда компилятора C # выбрала ephemeral для обозначения самой короткоживущей из недолговечных переменных, в то время как команда GC выбрала его для обозначения самой короткоживущей из долгоживущих переменных. Они не имеют ничего общего друг с другом, за исключением того, что в обоих случаях мы имеем в виду хранилище с более коротким сроком службы, чем можно было бы ожидать. - person Eric Lippert; 15.10.2019
comment
@EricLippert Спасибо за ваш ответ и правильные термины, такие как разыменование :-) Также очень интересная дискуссия с Лукой :-) - person stefan.at.wpf; 16.10.2019