Действительно ли экземпляры C # System.String оказываются в куче? - PullRequest
6 голосов
/ 06 июня 2019

Давайте рассмотрим очень простой код C #:

static void Main(string[] args)
        {
            int i = 5;
            string s = "ABC";
            bool b = false;
        }

Джеффри Рихтера " CLR via C # " (глава 14) утверждает, что " Тип String происходит непосредственно из Object, что делает его ссылочным типом, и, следовательно, объектами String (его массивом символы) всегда живут в куче, никогда в стеке потока".

Также ссылаясь на строки, на примере в книге, очень похожем на приведенный выше: « Инструкция newobj IL создает новый экземпляр объекта. Однако в примере кода IL инструкция newobj не появляется. Вместо этого вы видите специальную инструкцию ldstr (load string) IL, которая создает объект String с использованием литеральной строки, полученной из метаданных, что показывает, что общеязыковая среда исполнения (CLR) действительно имеет особый способ конструирования литерала Строковые объекты."

Глядя на код IL, это явно так (показана только соответствующая часть):

[...]
    .locals init (
        [0] int32,
        [1] string,
        [2] bool
    )
    // (no C# code)
    IL_0000: nop
    // int num = 5;
    IL_0001: ldc.i4.5
    IL_0002: stloc.0
    // string text = "ABC";
    IL_0003: ldstr "ABC"
    IL_0008: stloc.1
    // bool flag = false;
[...]

Инструкция ldstr IL гарантирует, что" ссылка на строку объекта помещается в стек ". Что имеет смысл - экземпляр строки остается в куче, а ссылка на этот объект (его адрес) сохраняется переменной в стеке.

Теперь давайте установим точку останова на строке, следующей за объявленной переменной text, начнем отладку в Visual Studio, а затем переключимся в представление «Разборка». Ниже приведен соответствующий код (полный разобранный код здесь ):

017B0483  nop  
            int i = 5;
017B0484  mov         dword ptr [ebp-40h],5  
            string s = "ABC";
017B048B  mov         eax,dword ptr ds:[429231Ch]  
017B0491  mov         dword ptr [ebp-44h],eax  
            bool b = false;
017B0494  xor         edx,edx  
017B0496  mov         dword ptr [ebp-48h],edx  
        }

Рассматривая конкретно две инструкции по сборке, обрабатывающие строку C # string, первая перемещает содержимое виртуальной памяти в 429231C в регистр eax, а вторая сохраняет соответствующий контент в стеке, где живет переменная s.

Давайте использовать WinDbg (x86, поскольку код C # использует 32-битную целевую платформу VS по умолчанию), чтобы посмотреть на этот конкретный адрес, подключив его к процессу, отлаживаемому VS, в неинвазивном режиме. Содержимое 429231C выше должно быть ссылкой на пространство памяти, в котором находится строка. Давайте проверим:

enter image description here

Вторая команда выдает 41, 42 и 43 в шестнадцатеричном виде, которые представляют A, B и C в ASCII; однако порядок не в порядке и может быть просто совпадением. (1) Не похоже, что ассемблерный код для строки строки все делает правильно.

Если мы используем VMMap для просмотра этого адреса: enter image description here

Исходный адрес 429231C выглядит в управляемой куче. Но тогда (2) почему содержимое адреса в куче должно быть занесено как ссылка, содержащаяся в переменной стека, как ранее указывалось в коде сборки?

2 вопрос с Я задаю (1) и (2). Несмотря на то, что все имеет смысл для меня, вплоть до анализа кода IL, все идет быстро, как только я смотрю на разобранный код для этого IL. Я склонен думать, что я что-то напутал в моей логике (скорее всего) или что-то вроде ошибки в отладчике VS (менее вероятно).

Позднее обновление : Как очень хорошо отметили @madreflection и @Jester, порядковый номер сбил меня с толку. В шестнадцатеричном представлении все в порядке. Теперь остается только вопрос (2) .

Позднее обновление 2 : комментарии были довольно проницательными, и я думаю, что @madreflection лучше всего это объясняет - существует дополнительный уровень косвенности - и причины для этого (указанные в комментариях) начинают имеет смысл для меня сейчас. Быстрая диаграмма ниже. Я также проверил, что оба адреса действительно принадлежат управляемой куче с VMMap.

enter image description here

Позднее обновление 3 : исправлена ​​предыдущая диаграмма.

...