Московский государственный технический университет им. Н.Э. Баумана
Опубликован: 28.06.2006 | Доступ: свободный | Студентов: 12464 / 341 | Оценка: 4.54 / 3.83 | Длительность: 22:03:00
ISBN: 978-5-9556-0055-0
Лекция 2:

Виртуальная система выполнения. Автоматическое управление памятью

< Лекция 1 || Лекция 2: 123 || Лекция 3 >

Состояние метода

На рис. 1.10 изображена схема состояния метода. Элементы состояния метода можно условно разделить на две группы: изменяемые данные и неизменяемые данные. Изменяемые данные доступны из тела метода для чтения и записи, в то время как неизменяемые данные либо доступны только для чтения либо вообще предназначены для внутреннего использования в системе выполнения.

Состояние метода

Рис. 1.10. Состояние метода

Давайте перечислим элементы состояния метода, входящие в группу изменяемых данных:

  • Указатель инструкции (Instruction Pointer).

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

  • Стек вычислений (Evaluation Stack).

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

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

  • Локальные переменные (Local Variable Array).

    Для хранения локальных переменных в состоянии метода предусмотрена отдельная область памяти, состоящая из так называемых слотов (slots). Каждой локальной переменной соответствует свой слот. Значения локальных переменных сохраняются при вызове методов аналогично содержимому стека вычислений.

  • Параметры (Argument Array).

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

  • Область локальных данных (Local Memory Pool).

    В языке CIL предусмотрена инструкция localloc, которая позволяет динамически размещать объекты в области памяти, локальной для метода. Объекты в этой области живут до тех пор пока метод не завершится.

Обратите внимание, что стек вычислений, локальные переменные и параметры, а также локальные данные метода представляют собой логически отдельные области памяти. Каждая конкретная реализация CLI самостоятельно решает вопрос, где размещать эти области.

В группу неизменяемых данных входят следующие элементы состояния метода:

  • Описатель метода (methodInfo handle).

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

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

  • Описатель безопасности (Security Descriptor).

    Используется системой безопасности CLI и недоступен из кода метода.

  • Состояние возврата (Return State Handle).

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

Стек вычислений

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

Стек вычислений в VES состоит из слотов. При этом глубина стека (максимальное количество слотов) всегда ограничена и задается статически в заголовке метода. Решение ограничить глубину стека было принято разработчиками спецификации CLI для того, чтобы облегчить создание JIT-компиляторов.

На входе метода стек вычислений всегда пуст. Затем он используется для передачи операндов инструкциям CIL, для передачи фактических параметров вызываемым методам, а также для получения результатов выполнения инструкций и вызываемых методов. Если метод возвращает какое-то значение, то оно кладется на стек вычислений перед завершением метода.

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

Каждый слот стека вычислений может содержать ровно одно значение одного из следующих типов:

  • int64 - 8-байтовое целое со знаком;
  • int32 - 4-байтовое целое со знаком;
  • native int - знаковое целое, разрядность которого зависит от аппаратной платформы (может быть 4 или 8 байт);
  • F - число с плавающей точкой, разрядность которого зависит от аппаратной платформы (не может быть меньше 8 байт);
  • & - управляемый указатель;
  • O - объектная ссылка;
  • Пользовательский тип-значение.

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

Также мы можем видеть, что допустимые типы значений для стека вычислений не совпадают с общей системой типов CTS. Например, в CTS существуют целые типы разрядности 1 и 2 байта, которые не могут содержаться на стеке вычислений. И наоборот, тип F стека вычислений не имеет аналога в CTS. Кроме того, для стека вычислений все управляемые указатели и объектные ссылки отображаются в два типа: & и O соответственно.

Давайте обсудим, как в VES осуществляется работа с типами данных, не поддерживаемыми напрямую стеком вычислений.

Во-первых, короткие целые типы ( bool, char, int8, int16, unsigned int8, unsigned int16 ) при загрузке на стек вычислений расширяются до int32. При этом знаковые короткие целые типы ( int8, int16 ) расширяются с сохранением знака, а беззнаковые расширяются путем добавления нулевых битов. При сохранении значения со стека вычислений в переменной, параметре, поле объекта или элементе массива происходит обратное сужающее преобразование.

Во-вторых, беззнаковый тип unsigned int32 при загрузке на стек вычислений становится знаковым int32, и аналогично, беззнаковый unsigned int64 становится знаковым int64. При этом, естественно, никаких преобразований не происходит - просто последовательность бит, которая раньше считалась беззнаковым целым, копируется на стек вычислений. Вообще говоря, утверждение, что целые типы int32, int64 и native int на стеке вычислений имеют знак, достаточно спорно. Правильнее было бы сказать, что они могут представлять как знаковые, так и беззнаковые целые числа в зависимости от того, какие инструкции CIL используются для их обработки.

В-третьих, типы float32 и float64 при копировании на стек вычислений преобразуются к типу F. Разрядность этого типа определяется конкретной реализацией CLI, которая, однако, должна гарантировать, что точность типа F не ниже, чем точность типа float64.

В-четвертых, типы-перечисления при копировании на стек вычислений автоматически превращаются в целые типы. Вообще, VES устроена таким образом, что типы-перечисления и целые типы являются совместимыми по присваиванию. Этим они отличаются от обычных типов-значений, которые при копировании на стек сохраняют свой тип и не совместимы с целыми типами.

И, наконец, для VES не имеет значения, какой точный тип имеют управляемые указатели и объектные ссылки. Любой управляемый указатель считается имеющим тип &, а любая объектная ссылка представляется типом O. Это означает, что согласно спецификации CLI система выполнения не обязана отслеживать правильность типов управляемых указателей и объектных ссылок. Действительно, контроль за правильностью типов находится в компетенции верификатора.

Локальные переменные и параметры

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

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

Если один и тот же слот стека вычислений в разные моменты времени может содержать данные разных типов, то слоты, используемые для хранения переменных и параметров, строго типизированы, но зато поддерживают все типы, определенные в спецификации CTS. Типы локальных переменных и параметров задаются в заголовке метода и доступны во время выполнения программы через описатель метода. Специальный флаг, также находящийся в заголовке метода, показывает, нужно ли обнулять локальные переменные при входе в метод.

Слоты, из которых состоят массивы переменных и параметров, адресуемы. Это означает, что в языке CIL существуют специальные инструкции, позволяющие получить адрес локальной переменной или параметра метода в виде управляемого указателя.

Компилятор, генерирующий CIL-код, не должен делать никаких предположений о том, как переменные и параметры размещены в памяти. Дело в том, что реализации CLI могут любым образом переупорядочивать переменные и параметры, могут произвольно выравнивать их в памяти и даже использовать для их хранения регистры процессора.

Область локальных данных

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

Память из области локальных данных может быть явно выделена с помощью инструкции localloc. Так как в языке CIL не существует инструкции, освобождающей память в области локальных данных, то эту область невозможно использовать для реализации менеджера памяти общего назначения.

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

В верифицированном коде использование области локальных данных запрещено.

< Лекция 1 || Лекция 2: 123 || Лекция 3 >
Анастасия Булинкова
Анастасия Булинкова
Рабочим названием платформы .NET было