Руководства, Инструкции, Бланки

Int 389 Инструкция img-1

Int 389 Инструкция

Категория: Инструкции

Описание

Компиляция Спецификации VM для виртуальной машины Java

Компиляция для виртуальной машины Java

Виртуальная машина Java разрабатывается, чтобы поддерживать язык программирования Java. Выпуски JDK Sun и Java, 2 SDK содержат обоих компилятор из исходного кода, записанного в языке программирования Java набору команд виртуальной машины Java, и системе времени выполнения, которая реализует виртуальную машину Java непосредственно. Понимание, как один компилятор использует виртуальную машину Java, полезно для возможного разработчика компилятора, так же как для одной попытки понять виртуальную машину Java непосредственно.

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

Отметьте, что термин "компилятор" иногда используется, обращаясь к транслятору от набора команд виртуальной машины Java к набору команд определенного ЦП. Одним примером такого транслятора является своевременное (JIT) генератор кода, который генерирует специфичные для платформы инструкции только после того, как код виртуальной машины Java был загружен. Эта глава не решает проблемы, связанные с генерацией кода, только связанные с компиляцией исходного кода, записанного в языке программирования Java инструкциям виртуальной машины Java.

7.1 Формат Примеров

Эта глава состоит, главным образом, из примеров исходного кода вместе с аннотируемыми списками кода виртуальной машины Java что javac компилятор в выпуске 1.0.2 JDK Sun генерирует для примеров. Код виртуальной машины Java пишется на неофициальном "ассемблере виртуальной машины", выведенном Sun javap утилита, распределенная с программным обеспечением JDK и Java 2 SDK. Можно использовать javap генерировать дополнительные примеры скомпилированных методов.

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

<Индекс> индекс кода операции инструкции в массиве, который содержит байты кода виртуальной машины Java для этого метода. Альтернативно, <индекс> может считаться байтовым смещением с начала метода. <Код операции> мнемосхема для кода операции инструкции, и нуль, или больше <operandN> является операндами инструкции. Дополнительное <комментарий> дается в синтаксисе комментария конца строки:

Часть материала в комментариях испускается javap ; остальное предоставляется авторами. <Индекс>, снабжающий каждую инструкцию предисловием, может использоваться в качестве цели инструкции передачи управления. Например, goto 8 передач инструкции управляет к инструкции по индексу 8. Отметьте, что фактические операнды инструкций передачи управления виртуальной машиной Java являются смещениями от адресов кодов операций тех инструкций; эти операнды выводятся на экран javap (и показываются в этой главе), как более легко смещения чтения в их методы.

Мы снабжаем предисловием операнд, представляющий индекс пула константы этапа выполнения с хешем, подписывают и следуют инструкциям комментарием, идентифицирующим элемент пула константы этапа выполнения, на который ссылаются, как в

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

7.2 Использование Констант, Локальных переменных, и Конструкций Управления

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

spin метод просто вращается вокруг пустого for цикл 100 раз:

Компилятор мог бы скомпилировать spin к

Виртуальная машина Java со стековой организацией с большинством операций, берущих один или более операндов от стека операнда текущего фрейма виртуальной машины Java или продвигающих результаты назад на стек операнда. Новый фрейм создается каждый раз, когда метод вызывается, и с этим создается новый стек операнда и набор локальных переменных для использования тем методом (см. Раздел 3.6, "Фреймы" ). В любой точке вычисления, таким образом, вероятно, будет много фреймов и одинаково много стеков операнда на поток управления, соответствуя многим вложенным вызовам метода. Только стек операнда в текущем фрейме является активным.

Набор команд виртуальной машины Java отличает типы операнда при использовании отличных байт-кодов для операций на его различных типах данных. Метод spin работает только на значениях типа int. Инструкции в его скомпилированном коде, выбранном, чтобы работать на введенных данных (iconst_0, istore_1, iinc, iload_1, if_icmplt), все специализируются для типа int .

Эти две константы в spin. 0 и 100. продвигаются на стек операнда, используя две различных инструкции. 0 продвигается, используя iconst_0 инструкцию, одно из семейства iconst _ <i> инструкции. 100 продвигается, используя bipush инструкцию, которая выбирает значение, которое она продвигает как непосредственный операнд.

Виртуальная машина Java часто использует в своих интересах вероятность определенных операндов ( int константы-1, 0, 1, 2, 3, 4 и 5 в случае iconst _ <i> инструкции), делая те операнды, неявные в коде операции. Поскольку iconst_0 инструкция знает, что собирается продвинуть int 0. iconst_0 не должен сохранить операнд, чтобы сказать это, какое значение продвинуть, и при этом это не должно выбрать или декодировать операнд. Компиляция нажатия 0 как bipush 0 было бы корректно, но сделает скомпилированный код для spin на один байт дольше. Простая виртуальная машина также провела бы дополнительное время, выбирая и декодируя явный операнд каждый раз вокруг цикла. Использование неявных операндов делает скомпилированный код более компактным и эффективным.

int i в spin сохранен как локальная переменная виртуальной машины Java 1. Поскольку большинство инструкций виртуальной машины Java работает на значениях, вытолканных от стека операнда, а не непосредственно на локальных переменных, инструкции, которые передают значения между локальными переменными и стеком операнда, распространены в коде, скомпилированном для виртуальной машины Java. У этих операций также есть специальная поддержка в наборе команд. В spin. значения передаются и от локальных переменных, используя istore_1 и iload_1 инструкции, каждая из которых неявно работает на локальной переменной 1. istore_1 инструкция появляется int от стека операнда и хранилищ это в локальной переменной 1. iload_1 инструкция продвигает значение в локальной переменной 1 на стек операнда.

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

Определенным очень частым операциям на локальных переменных угождает особенно виртуальная машина Java. iinc инструкция постепенно увеличивает содержание локальной переменной однобайтовым значением со знаком. iinc инструкция в spin постепенно увеличивает первую локальную переменную (ее первый операнд) 1 (ее второй операнд). iinc инструкция очень удобна, реализовывая конструкции цикличного выполнения.

for цикл spin выполняется, главным образом, этими инструкциями:

bipush инструкция продвигает значение 100 на стек операнда как int. тогда if_icmplt инструкция выталкивает то значение от стека операнда и сравнивает это со мной. Если сравнение успешно выполняется (переменная i меньше чем 100 ), управление передается индексу 5 и следующей итерации for цикл начинается. Иначе, управляйте передачами в инструкцию после if_icmplt.

Если spin пример использовал тип данных кроме int для счетчика цикла скомпилированный код обязательно изменился бы, чтобы отразить различный тип данных. Например, если вместо int spin пример использует a double. как показано,

Инструкции, которые работают на введенных данных, теперь специализируются для типа double. (ldc2_w инструкция будет обсуждена позже в этой главе.)

Вспомните это double значения занимают две локальных переменные, хотя к ним только получают доступ, используя меньший индекс этих двух локальных переменных. Это также имеет место для значений типа long. Снова например,

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

Размер кода операции виртуальной машины Java 1-байтовых результатов в его скомпилированном коде, являющемся очень компактным. Однако, 1-байтовые коды операций также означают, что набор команд виртуальной машины Java должен остаться небольшим. Как компромисс, виртуальная машина Java не оказывает равную поддержку для всех типов данных: это не абсолютно ортогонально (см. Таблицу 3.2, "Поддержка типа в наборе команд виртуальной машины Java" ).

Например, сравнение значений типа int в for оператор примера spin может быть реализован, используя единственную if_icmplt инструкцию; однако, нет никакой единственной инструкции в наборе команд виртуальной машины Java, который выполняет условный переход на значениях типа double. Таким образом, dspin должен реализовать его сравнение значений типа double использование dcmpg инструкции следовало iflt инструкцией.

Виртуальная машина Java оказывает самую прямую поддержку для данных типа int. Это частично в ожидании эффективных реализаций стеков операнда виртуальной машины Java и массивов локальной переменной. Это также мотивируется частотой int данные в типичных программах. У других целочисленных типов есть менее прямая поддержка. Есть нет byte. char. или short версии хранилища, загрузки, или добавляют инструкции, например. Вот spin пример, записанный, используя a short :

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

Нехватка прямой поддержки byte. char. и short вводит виртуальную машину Java, не является особенно болезненным, потому что значения тех типов внутренне продвигаются на int ( byte и short расширяются до знака на int. char расширяется до нуля). Операции на byte. char. и short данные могут таким образом быть сделаны, используя int инструкции. Единственная дополнительная стоимость является стоимостью усечения значений int операции к допустимым диапазонам.

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

7.3 Арифметика

Виртуальная машина Java обычно делает арифметику на своем стеке операнда. (Исключение является iinc инструкцией, которая непосредственно постепенно увеличивает значение локальной переменной.) Например, align2grain метод выравнивается int оцените данному питанию 2:

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

(grain - 1) обрабатывается этими инструкциями:

Сначала grain - 1 вычисляется, используя содержание локальной переменной 2 и непосредственное int значение 1. Эти операнды выталкиваются от стека операнда и их различия, пододвинутого обратно на стек операнда. Различие таким образом сразу доступно для использования в качестве одного операнда ixor инструкции. (Вспомните это

x == - 1^x .) Точно так же результат ixor инструкции становится операндом для последующей iand инструкции.

Код для всего метода следует:

7.4 Доступ к Пулу Константы этапа выполнения

Ко многим числовым константам, так же как объектам, полям, и методам, получают доступ через пул константы этапа выполнения текущего класса. Объектный доступ рассматривают позже (§7.8). Данные типов int. long. float. и double. так же как ссылки на экземпляры класса String. управляются, используя ldc, ldc_w, и ldc2_w инструкции.

ldc и ldc_w инструкции привыкли к значениям доступа в пуле константы этапа выполнения (включая экземпляры класса String ) из типов кроме double и long. ldc_w инструкция используется вместо ldc только, когда есть большое количество элементов пула константы этапа выполнения, и больший индекс необходим, чтобы получить доступ к элементу. ldc2_w инструкция используется, чтобы получить доступ ко всем значениям типов double и long ; нет никакой неширокой разновидности.

Интегральные константы типов byte. char. или short. так же как маленький int значения, может быть скомпилирован, используя bipush, sipush, или iconst _ <i> инструкции, как замечено ранее (§7.2). Определенные маленькие константы с плавающей точкой могут быть скомпилированы, используя fconst _ <f> и dconst _ <d> инструкции.

Во всех этих случаях компиляция является прямой. Например, константы для

устанавливаются следующим образом:

7.5 Больше Примеров Управления

Компиляция for операторы показали в более раннем разделе (§7.2). Большая часть языка программирования Java другие конструкции управления ( if-then-else. do. while. break. и continue ) также компилируются очевидными способами. Компиляция switch операторы обрабатываются в отдельном участке (Раздел 7.10, "Компилируя Переключатели" ), как компиляция исключений (Раздел 7.12, "Бросая и Обрабатывая Исключения" ) и компиляция finally пункты (Раздел 7.13, "Компилируя наконец " ).

Как дальнейший пример, a while цикл компилируется очевидным способом, хотя определенные инструкции передачи управления, сделанные доступный виртуальной машиной Java, изменяются типом данных. Как обычно есть больше поддержки данных типа int. например:

Отметьте что тест while оператор (реализованное использование if_icmplt инструкции) у основания кода виртуальной машины Java для цикла. (Это также имело место в spin примеры ранее.) Тест, являющийся у основания цикла, вынуждает использование goto инструкции добраться до теста до первой итерации цикла. Если тот тест перестал работать, и тело цикла никогда не вводится, эта дополнительная инструкция тратится впустую. Однако, while циклы обычно используются, когда их тело, как ожидают, будет выполнено, часто для многих итераций. Для последующих итераций, помещая тест у основания цикла экономит инструкции виртуальной машины Java каждое время вокруг цикла: если бы тест был наверху цикла, то тело цикла нуждалось бы в запаздывании goto инструкция, чтобы возвратиться к вершине.

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

У каждого типа с плавающей точкой есть две инструкции сравнения: fcmpl и fcmpg для типа float. и dcmpl и dcmpg для типа double. Разновидности отличаются только по их обработке НЭН. НЭН Неупорядочивают, таким образом, все сравнения с плавающей точкой перестали работать, если любым из их операндов является НЭН. Компилятор выбирает разновидность инструкции сравнения для соответствующего типа, который приводит к тому же самому результату, оценивают ли сбои сравнения на NON-НЭН или встречаются с НЭН.

Если d не НЭН и меньше чем 100.0. dcmpg инструкция продвигает int -1 на стек операнда, и ifge инструкцию не переходит. Ли d больше чем 100.0 или НЭН, dcmpg инструкция продвигает int 1 на стек операнда, и ответвления ifge. Если d равно 100.0. dcmpg инструкция продвигает int 0 на стек операнда, и ответвления ifge.

dcmpl инструкция достигает того же самого эффекта, если сравнение инвертируется:

Еще раз, ли сравнение перестало работать на значении NON-НЭН или потому что его передают НЭН, dcmpl инструкция продвигает int значение на стек операнда, который заставляет ifle переходить. Если бы обе из dcmp инструкций не существовали, один из методов в качестве примера должен был бы сделать больше работы, чтобы обнаружить НЭН.

7.6 Получение Параметров

Если n параметры передают к методу экземпляра, они получаются, условно, в локальных переменных, пронумерованных 1 через n фрейма, создаваемого для нового вызова метода. Параметры получаются в порядке, их передали. Например:

Условно, метод экземпляра передают a reference к его экземпляру в локальной переменной 0. В языке программирования Java экземпляр доступен через this ключевое слово.

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

Единственная разница - то, что параметры метода кажутся запускающимися в локальной переменной 0, а не 1.

7.7 Вызов Методов

Нормальный вызов метода для метода экземпляра диспетчеризирует на типе времени выполнения объекта. (Они являются виртуальными в сроках C++.) Такой вызов реализуется, используя invokevirtual инструкцию, которая берет в качестве ее параметра индекс к записи пула константы этапа выполнения, дающей полностью определенное имя типа класса объекта, имя метода, чтобы вызвать, и что дескриптор метода (§4.3.3). Вызвать addTwo метод, определенный ранее как метод экземпляра, мы могли бы записать

Это компилирует в

Вызов устанавливается первым продвижением a reference к текущему экземпляру, this. на стек операнда. Параметры вызова метода, int значения 12 и 13. тогда продвигаются. Когда фрейм для addTwo метод создается, параметры, которые передают к методу, становятся начальными значениями локальных переменных нового фрейма. Таким образом, reference для this и эти два параметра, продвинутые на стек операнда invoker, станут начальными значениями локальных переменных 0, 1, и 2 из вызванного метода.

Наконец, addTwo вызывается. Когда это возвращается, int возвращаемое значение продвигается на стек операнда фрейма invoker, add12and13 метод. Возвращаемое значение таким образом кладется на место, чтобы быть сразу возвращенным к invoker add12and13.

Возврат из add12and13 обрабатывается ireturn инструкцией add12and13. ireturn инструкция берет int значение, возвращенное addTwo. на стеке операнда текущего фрейма, и нажатиях это на стек операнда фрейма invoker. Это тогда возвращает управление invoker, делая ток фрейма invoker. Виртуальная машина Java обеспечивает отличные инструкции возврата для многих из его числовых и reference типы данных, так же как инструкция возврата для методов без возвращаемого значения. Тот же самый набор инструкций возврата используется для всех вариантов вызовов метода.

Операнд invokevirtual инструкции (в примере, индекс пула константы этапа выполнения #4) не является смещением метода в экземпляре класса. Компилятор не знает внутреннее расположение экземпляра класса. Вместо этого это генерирует символьные ссылки на методы экземпляра, которые сохранены в пуле константы этапа выполнения. Те элементы пула константы этапа выполнения разрешаются во время выполнения, чтобы определить фактическое расположение метода. То же самое является истиной для всех других инструкций виртуальной машины Java тот класс доступа экземпляры.

Вызов addTwoStatic. класс ( static ) разновидность addTwo. подобно, как показано:

хотя различная инструкция вызова метода виртуальной машины Java используется:

Компиляция вызова класса ( static ) метод очень походит на компиляцию вызова метода экземпляра, кроме this не передается invoker. Параметры метода будут таким образом получены, начинаясь с локальной переменной 0 (см. Раздел 7.6, "Получая Параметры" ). invokestatic инструкция всегда используется, чтобы вызвать методы класса.

invokespecial инструкция должна использоваться, чтобы вызвать методы инициализации экземпляра (см. Раздел 7.8, "Работающий с Экземплярами Класса" ). Это также используется, вызывая методы в суперклассе ( super ) и вызывая private методы. Например, данный классы Near и Far объявленный как

метод Near.getItNear (который вызывает a private метод), становится

Метод Far.getItFar (который вызывает метод суперкласса), становится

Отметьте, что методы вызывали использование invokespecial инструкции, всегда передают this к вызванному методу как его первый параметр. Как обычно это получается в локальной переменной 0.

7.8 Работа с Экземплярами Класса

Экземпляры класса виртуальной машины Java создаются, используя новую инструкцию виртуальной машины Java. Вспомните, что на уровне виртуальной машины Java, конструктор появляется как метод с предоставленным компилятор именем <init>. Этот особенно именованный метод известен как метод инициализации экземпляра (§3.9). Многократные методы инициализации экземпляра, соответствуя многократным конструкторам, могут существовать для данного класса. Как только экземпляр класса был создан и его переменные экземпляра, включая таковые из класса и всех его суперклассов, был инициализирован к их значениям по умолчанию, метод инициализации экземпляра нового экземпляра класса вызывается. Например:

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

К полям экземпляра класса (переменные экземпляра) получают доступ, используя getfield и putfield инструкции. Если i переменная экземпляра типа int. методы setIt и getIt, определенный как

Как с операндами инструкций вызова метода, операнды putfield и getfield инструкций (индекс пула константы этапа выполнения #4) не являются смещениями полей в экземпляре класса. Компилятор генерирует символьные ссылки на поля экземпляра, которые сохранены в пуле константы этапа выполнения. Те элементы пула константы этапа выполнения разрешаются во время выполнения, чтобы определить расположение поля в пределах объекта, на который ссылаются.

7.9 Массивы

Массивы виртуальной машины Java являются также объектами. Массивы создаются и управляли использованием отличного набора инструкций. newarray инструкция используется, чтобы создать массив числового типа. Код

мог бы быть скомпилирован в

anewarray инструкция используется, чтобы создать одномерный массив ссылок на объект, например:

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

Первый операнд multianewarray инструкции является индексом пула константы этапа выполнения к типу класса массива, который будет создаваться. Вторым является число размерностей того типа массива, чтобы фактически создать. multianewarray инструкция может использоваться, чтобы создать все размерности типа как код для create3DArray шоу. Отметьте, что многомерный массив является только объектом и так загружается и возвращается aload_1 и areturn инструкцией, соответственно. Для получения информации об именах классов массива см. Раздел 4.4.1.

Все массивы связали длины, к которым получают доступ через arraylength инструкцию.

7.10 Переключателей Компиляции

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

tableswitch виртуальной машины Java и lookupswitch инструкции работают только на int данные. Поскольку операции на byte. char. или short значения внутренне продвигаются на int. a switch то, выражение которого оценивает к одному из тех типов, компилируется, как если бы это оценило, чтобы ввести int. Если chooseNear метод был записан, используя тип short. те же самые инструкции виртуальной машины Java были бы сгенерированы, используя тип int. Другие числовые типы должны быть сужены, чтобы ввести int для использования в a switch .

Где случаи switch редки, табличное представление tableswitch инструкции становится неэффективным с точки зрения пространства. lookupswitch инструкция может использоваться вместо этого. lookupswitch пары инструкции int ключи (значения case метки) с целевыми смещениями в таблице. Когда lookupswitch инструкция выполняется, значение выражения switch сравнивается с ключами в таблице. Если один из ключей соответствует значение выражения, выполнение продолжается при связанном целевом смещении. Если никакой ключ не соответствует, выполнение продолжается в default цель. Например, скомпилированный код для

взгляды точно так же как код для chooseNear. за исключением использования lookupswitch инструкции:

Виртуальная машина Java определяет, что таблица lookupswitch инструкции должна быть сортирована ключом так, чтобы реализации могли использовать поиски, более эффективные чем линейное сканирование. Даже в этом случае lookupswitch инструкция должна искать свои ключи соответствие, а не просто выполнить граничную проверку и индекс в таблицу как tableswitch. Таким образом tableswitch инструкция, вероятно, более эффективна чем lookupswitch, где соображения пространства разрешают выбор.

7.11 Операций на Стеке Операнда

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

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

7.12 Бросков и Обработка Исключений

Исключения выдаются из программ, используя throw ключевое слово. Его компиляция проста:

Компиляция try - catch конструкции являются прямыми. Например,

Смотря более близко, try блок компилируется, как это было бы если try не присутствовали:

Если никакое исключение не выдается во время выполнения try блок, это ведет себя как если бы try не были ли: tryItOut вызывается и catchOne возвраты.

После try блок является кодом виртуальной машины Java, который реализует сингл catch пункт:

Вызов handleExc. содержание catch пункт, также компилируется как нормальный вызов метода. Однако, присутствие a catch пункт заставляет компилятор генерировать запись таблицы исключений. Таблица исключений для catchOne у метода есть одна запись, соответствующая одному параметру (экземпляр класса TestExc ) то, что catch пункт catchOne может обработать. Если некоторое значение, которое является экземпляром TestExc бросается во время выполнения инструкций между индексами 0 и 4 в catchOne. управление передается коду виртуальной машины Java по индексу 5, который реализует блок catch пункт. Если значение, которое бросается, не является экземпляром TestExc. catch пункт catchOne не может обработать это. Вместо этого значение повторно бросается в invoker catchOne.

A try может иметь многократный catch пункты:

Многократный catch пункты данного try оператор компилируется, просто добавляя код виртуальной машины Java для каждого catch пункт один за другим и добавляющие записи в таблицу исключений, как показано:

Если во время выполнения try пункт (между индексами 0 и 4) значение бросается, который соответствует параметр один или больше catch пункты (значение является экземпляром один или больше параметров), первое (самое внутреннее) такой catch пункт выбирается. Управление передается коду виртуальной машины Java для блока этого catch пункт. Если брошенное значение не соответствует параметр какого-либо из catch пункты catchTwo. виртуальная машина Java повторно бросает значение, не вызывая код в любом catch пункт catchTwo.

Вложенный try - catch операторы компилируются очень как a try оператор с многократным catch пункты:

Вложение catch пункты представляются только в таблице исключений. Когда исключение выдается, первый (самый внутренний) пункт выгоды, который содержит сайт исключения и с соответствующим параметром, выбирается, чтобы обработать это. Например, если вызов tryItOut (по индексу 1), бросил экземпляр TestExc1. это было бы обработано catch пункт, который вызывает handleExc1. Это так даже при том, что исключение происходит в пределах границ внешнего catch пункт (ловля TestExc2 ) и даже при том, что это внешнее catch пункт, возможно, иначе был в состоянии обработать брошенное значение.

Как тонкий момент, отметьте что диапазон a catch пункт является содержащим на "от" конца и монопольным на, чтобы закончиться (§4.7.3). Таким образом, запись таблицы исключений для catch ловля пункта TestExc1 не покрывает инструкцию возврата при смещении 4. Однако, запись таблицы исключений для catch ловля пункта TestExc2 действительно покрывает инструкцию возврата при смещении 11. Возвратите инструкции в пределах вложенного catch пункты включаются в диапазон инструкций, покрытых вложением catch пункты.

7.13 Компиляции finally

Компиляция a try - finally оператор подобен тому из try-catch. До передачи управления вне try оператор, является ли та передача нормальной или резкой, потому что исключение было выдано, finally пункт должен сначала быть выполнен. Для этого простого примера

Есть четыре пути к управлению, чтобы передать за пределами try оператор: проваливаясь нижняя часть того блока, возвращаясь, выполняясь a break или continue оператор, или повышая исключение. Если tryItOut возвраты, не повышая исключение, управление передается finally блок используя jsr инструкцию. jsr 14 инструкций по индексу 4 делает "вызов подпрограммы" к коду для finally блок по индексу 14 ( finally блок компилируется как встроенная подпрограмма). Когда finally блок завершается, мочение 2 управления возвратами инструкции к инструкции, следующей jsr инструкциям по индексу 4.

Более подробно вызов подпрограммы работает следующим образом: jsr инструкция продвигает адрес следующих инструкций (возврат по индексу 7) на стек операнда перед переходом. astore_2 инструкция, которая является целью перехода, хранит адрес на стеке операнда в локальную переменную 2. Код для finally блок (в этом случае aload_0 и invokevirtual инструкции) выполняется. Принятие выполнения того кода обычно завершается, мочить инструкция получает адрес от локальной переменной 2 и возобновляет выполнение в том адресе. Инструкция возврата выполняется, и tryFinally возвраты обычно.

A try оператор с a finally пункт компилируется, чтобы иметь специальный обработчик исключений, тот, который может обработать любое исключение, выданное в пределах try оператор. Если tryItOut выдает исключение, таблицу исключений для tryFinally ищется соответствующий обработчик исключений. Специальный обработчик находится, заставляя выполнение продолжаться по индексу 8. astore_1 инструкция по индексу 8 хранит брошенное значение в локальную переменную 1. Следующая jsr инструкция делает вызов подпрограммы к коду для finally блок. Предполагая, что код обычно возвращается, aload_1 инструкция по индексу 12 продвигает отброшенное значение назад на стек операнда, и следующая athrow инструкция повторно бросает значение.

Компиляция a try оператор с обоими a catch пункт и a finally пункт более сложен:

Если try оператор обычно завершается, goto инструкция при индексных 4 переходах к вызову подпрограммы для finally блок по индексу 16. finally блок по индексу 26 выполняется, управление возвращается к инструкции возврата по индексу 19, и tryCatchFinally возвраты обычно.

Если tryItOut бросает экземпляр TestExc. первый (самый внутренний) применимый обработчик исключений в таблице исключений выбирается, чтобы обработать исключение. Код для того обработчика исключений, начинающегося по индексу 7, передает брошенное значение к handleExc и по его возврату делает тот же самый вызов подпрограммы к finally блок по индексу 26 как в нормальном случае. Если исключение не выдается handleExc. tryCatchFinally возвраты обычно.

Если tryItOut бросает значение, которое не является экземпляром TestExc или если handleExc непосредственно выдает исключение, условие обрабатывается второй записью в таблице исключений, которая обрабатывает любое значение, брошенное между индексами 0 и 16. Тот обработчик исключений передает управление индексу 20, где брошенное значение сначала сохранено в локальной переменной 1. Код для finally блок по индексу 26 вызывают как подпрограмма. Если это возвращается, брошенное значение получается от локальной переменной 1 и повторно брошенное использование athrow инструкции. Если новое значение бросается во время выполнения finally пункт, finally аварийные прекращения работы пункта, и tryCatchFinally возвраты резко, бросая новое значение в его invoker.

7.14 Синхронизаций

Виртуальная машина Java оказывает явную поддержку для синхронизации через ее monitorenter и monitorexit инструкции. Для кода, записанного в языке программирования Java, однако, возможно, наиболее распространенная форма синхронизации synchronized метод.

A synchronized метод обычно не реализуется, используя monitorenter и monitorexit. Скорее это просто отличают в пуле константы этапа выполнения ACC_SYNCHRONIZED флаг, который проверяется инструкциями вызова метода. Вызывая метод, для который ACC_SYNCHRONIZED устанавливается, текущий поток получает монитор, вызывает метод непосредственно, и выпускает монитор, завершается ли вызов метода обычно или резко. В течение времени выполняющемуся потоку принадлежит монитор, никакой другой поток не может получить это. Если исключение выдается во время вызова synchronized метод и synchronized метод не обрабатывает исключение, монитор для метода автоматически выпускается прежде, чем исключение повторно бросается из synchronized метод.

monitorenter и monitorexit инструкции существуют, чтобы поддерживать synchronized операторы. Например:

7.15 Компиляции Вложенные Классы и Интерфейсы

Выпуск 1.1 JDK добавленные вложенные классы и интерфейсы к языку программирования Java. Вложенные классы и интерфейсы иногда упоминаются как внутренние классы и интерфейсы. которые являются одним видом вложенных классов и интерфейсов. Однако, вложенные классы и интерфейсы также охватывают вложенные высокоуровневые классы и интерфейсы, которые не являются внутренними классами или интерфейсами.

Полная обработка компиляции вложенных классов и интерфейсов выходит за рамки этой главы. Однако, заинтересованные читатели могут обратиться к Внутренней Спецификации Классов в http://java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc.html .

1 Эта goto инструкция является строго ненужной, но сгенерирована javac компилятор выпуска 1.0.2 JDK Sun.

Спецификация Виртуальной машиныJavaTM
Авторское право © Sun Microsystems, Inc 1999 года. Все права защищены
Пожалуйста, отправьте любые комментарии или исправления к jvm@java.sun.com

Другие статьи