Spec-Zone .ru
спецификации, руководства, описания, API
Содержание | Предыдущий | Следующий | ИндексСпецификация Виртуальной машины JavaTM


ГЛАВА 7

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


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

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

Мы полагаем, что ограниченные продолжения к будущим версиям виртуальной машины Java поддерживают более широкий диапазон языков более непосредственно. Пожалуйста, свяжитесь с нами в jvm@javasoft.com если у Вас есть интерес к этому усилию.

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


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

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

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


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

8
bipush 100
//Продвиньте постоянный 100


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

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

10
ldc #1
//Плавание 100.000000


или

9
invokevirtual #4
//Пример метода.addTwo(II)I


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


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

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

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


    void spin() {
    	int i;
    	for (i = 0; i < 100; i++) {
        ;   // Loop body is empty
    	}
    }

Компиляции компилятора Java spin к

Метод void spin()

0
iconst_0
// Push int constant 0
1
istore_1
// Store into local 1 (i=0)
2
goto 8
// First time through don't increment
5
iinc 1 1
// Increment local 1 by 1 (i++)
8
iload_1
// Push local 1 (i)
9
bipush 100
// Push int constant (100)
11
if_icmplt 5
// Compare, loop if < (i < 100)
14
return
//Возвратиться void когда сделано


Виртуальная машина Java со стековой организацией с большинством операций, берущих один или более операндов от стека операнда текущего фрейма виртуальной машины 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.

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

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

5
iinc 1 1
//Постепенно увеличьте локальный 1 1 (я ++)
8
iload_1
//Продвиньте локальный 1 (i)
9
bipush 100
//Продвиньте международную константу (100)
11
if_icmplt 5
//Сравнитесь, цикл если <(я <100)


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

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


    void dspin() {
    	double i;
    	for (i = 0.0; i < 100.0; i++) {
    	              ;	      // Loop body is empty
    	}
    }

скомпилированный код

Метод void dspin()

0
dconst_0
// Push double constant 0.0
1
dstore_1
// Store into locals 1 and 2 (i = 0.0)
2
goto 9
// First time through don't increment
5
dload_1
// Push double onto operand stack
6
dconst_1
// Push double constant 1 onto stack
7
dadd
// Add; there is no dinc instruction
8
dstore_1
// Store result in locals 1 and 2
9
dload_1
// Push local
10
ldc2_w #4
// Double 100.000000
13
dcmpg
// There is no if_dcmplt instruction
14
iflt 5
// Compare, loop if < (i < 100.000000)
17
return
// Return void when done


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

Отметьте это в dspin, double значения используют два слова хранения, ли на стеке операнда или в локальных переменных. Это также имеет место для значений типа long. Как другой пример:


    double doubleLocals(double d1, double d2) {
    	return d1 + d2;
    }

становится

Метод double doubleLocals(double,double)

0
dload_1
// First argument in locals 1 and 2
1
dload_3
// Second argument in locals 3 and 4
2
dadd
// Each also uses two words on stack
3
dreturn



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

Размер кода операции виртуальной машины Java однобайтовых результатов в его скомпилированном коде, являющемся очень компактным. Однако, однобайтовые коды операций также означают, что набор команд виртуальной машины Java должен остаться небольшим. Как компромисс, виртуальная машина Java не оказывает равную поддержку для всех типов данных: это не абсолютно ортогонально (см. Таблицу 3.1, "Поддержка типа в наборе команд виртуальной машины Java"). В случае dspin, отметьте, что нет никакой if_dcmplt инструкции в наборе команд виртуальной машины Java. Вместо этого сравнение должно быть выполнено, используя dcmpg, сопровождаемый iflt, требуя еще одной инструкции виртуальной машины Java чем int версия spin.

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

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


    void sspin() {
    	short i;
    	for (i = 0; i < 100; i++) {
    	    ;		// Loop body is empty
    	}
    }

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

метод void sspin()

0
iconst_0
1
istore_1
2
goto 10
5
iload_1
// The short is stored in an int
6
iconst_1
7
iadd
8
i2s
// Truncate int to short
9
istore_1
10
iload_1
11
bipush 100
13
if_icmplt 5
16
return


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

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


7.3 Арифметика

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


    int align2grain(int i, int grain) {
    	return ((i + grain-1) & ~(grain-1));
    }

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

5
iload_2
//Загрузка grain на стек операнда
6
iconst_1
//Постоянная загрузка 1 на стек операнда
7
isub
//Вычтите; продвиньте результат на стек
8
iconst_m1
//Постоянная загрузка -1 на стек операнда
9
ixor
//Сделайте XOR; продвиньте результат на стек


Сначала grain-1 вычисляется, используя содержание локальной переменной 2 и непосредственное int значение 1. Эти операнды выталкиваются от стека операнда и их различия, пододвинутого обратно на стек операнда, где это сразу доступно для использования в качестве одного операнда ixor инструкции (вспомните это ~x == -1^x). Точно так же результат ixor инструкции становится операндом для последующей iand инструкции.

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

Метод int align2grain(int,int)

0
iload_1
1
iload_2
2
iadd
3
iconst_1
4
isub
5
iload_2
6
iconst_1
7
isub
8
iconst_m1
9
ixor
10
iand
11
ireturn



7.4 Доступ к Постоянному Пулу

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

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

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

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


    void useManyNumeric() {
    	int i = 100;
    	int j = 1000000;
    	long l1 = 1;
    	long l2 = 0xffffffff;
    	double d = 2.2;
    	...do some calculations...
    }

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

Метод void useManyNumeric()

0
bipush 100
// Push a small int with bipush
2
istore_1
3
ldc #1
// Integer 1000000; a larger int


// value uses ldc
5
istore_2
6
lconst_1
// A tiny long value uses short, fast lconst_1
7
lstore_3
8
ldc2_w #6
// A long 0xffffffff (that is, an int -1); any


// long constant value can be pushed by ldc2_w
11
lstore 5
13
ldc2_w #8
// Double 2.200000; so do


// uncommon double values
16
dstore 7


    ...do those calculations...


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

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

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


    void whileInt() {
    	int i = 0;
    	while (i < 100) {
    	    i++;
    	}
    }

компилируется в

Метод void whileInt()

0
iconst_0
1
istore_1
2
goto 8
5
iinc 1 1
8
iload_1
9
bipush 100
11
if_icmplt 5
14
return


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

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


    void whileDouble() {
    	double i = 0.0;
    	while (i < 100.1) {
    	    i++;
    	}
    }

компилируется в

Метод void whileDouble()

0
dconst_0
1
dstore_1
2
goto 9
5
dload_1
6
dconst_1
7
dadd
8
dstore_1
9
dload_1
10
ldc2_w #4
// Double 100.100000
13
dcmpg
// To test we have to use
14
iflt 5
// two instructions...
17
return


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


    int lessThan100(double d) {
    	if (d < 100.0) {
    	    return 1;				
    	} else {
    	    return -1;				
    	}
    }

компиляции к

Метод int lessThan100(double)

0
dload_1
1
ldc2_w #4
// Double 100.000000
4
dcmpg
// Push 1 if d is NaN or d > 100.000000;


// push 0 if d == 100.000000
5
ifge 10
// Branch on 0 or 1
8
iconst_1
9
ireturn
10
iconst_m1
11
ireturn


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

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


    int greaterThan100(double d) {
    	if (d > 100.0) {
    	    return 1;			
    	} else {
    	    return -1;			
    	}
    }

становится

Метод int greaterThan100(double)

0
dload_1
1
ldc2_w #4
// Double 100.000000
4
dcmpl
// Push -1 if d is Nan or d < 100.000000;


//продвиньте 0 если d == 100.000000
5
ifle 10
//Ответвление по 0 или-1
8
iconst_1
9
ireturn
10
iconst_m1
11
ireturn


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


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

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


    int addTwo(int i, int j) {
    	return i + j;
    }

компиляции к

Метод int addTwo(int,int)

0
iload_1
// Push value of local 1 (i)
1
iload_2
// Push value of local 2 (j)
2
iadd
// Add; leave int result on val stack
3
ireturn
// Return int result


Условно, метод экземпляра передают a reference к его экземпляру в нуле локальной переменной. Экземпляр доступен в Java через this ключевое слово. Код, чтобы продвинуть this в локальную переменную нуль должен присутствовать в invoker метода экземпляра (см. Раздел 7.7, "Вызывая Методы").

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


    static int addTwoStatic(int i, int j) {
    	return i + j;
    }

компиляции к

Метод int addTwoStatic(int,int)

0
iload_0
1
iload_1
2
iadd
3
ireturn


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


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

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


    int add12and13() {
    	return addTwo(12, 13);
    }

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

Метод int add12and13()

0
aload_0
//Продвиньте эту локальную переменную 0 (this) на стек
1
bipush 12
//Продвинуть int постоянный 12 на стек
3
bipush 13
//Продвинуть int постоянный 13 на стек
5
invokevirtual #4
//Метод Example.addtwo(II)I
8
ireturn
//Возвратиться int сверху стека; это


// int результат 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 не знает внутреннее расположение экземпляра класса. Вместо этого это генерирует символьные ссылки на методы экземпляра, которые сохранены в постоянном пуле. Те постоянные элементы пула разрешаются во время выполнения, чтобы определить фактическое расположение метода. То же самое является истиной для всех других инструкций виртуальной машины Java тот класс доступа экземпляры.

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


    int add12and13() {
    	return addTwoStatic(12, 13);
    }

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

Метод int add12and13()

0
bipush 12
2
bipush 13
4
invokestatic #3
// Method Example.addTwoStatic(II)I
7
ireturn


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

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


    class Near {
    	int it;
    	public int getItNear() {
    		return getIt();
    	}
    	private int getIt() {
    		return it;
    	}

    }
    class Far extends Near {      int getItFar() {      return super.getItNear();      }     }
метод Near.getItNear (который вызывает a private метод), становится

Метод int getItNear()

0
aload_0

1
invokespecial #5
// Method Near.getIt()I
4
ireturn



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

Метод int getItFar()

0
aload_0
1
invokespecial #4
// Method Near.getItNear()I
4
ireturn


    

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


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

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


    Object create() {
    	return new Object();
    }

компиляции к

Метод java.lang.Object create()

0
new #1
// Class java.lang.Object
3
dup
4
invokespecial #4
// Method java.lang.Object.<init>()V
7
areturn


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


    int i; // An instance variable
    MyObj example() {
    	MyObj o = new MyObj();
    	return silly(o);
    }
    MyObj silly(MyObj o) {
    	if (o != null) {
    	    return o;
    	} else {
    	    return o;
    	}
    }

становится

Метод MyObj example()

0
new #2
// Class MyObj
3
dup
4
invokespecial #5
// Method MyObj.<init>()V
7
astore_1
8
aload_0
9
aload_1
10
invokevirtual #4


// Method Example.silly(LMyObj;)LMyObj;
13
areturn


Метод MyObj silly(MyObj)

0
aload_1
1
ifnull 6
4
aload_1
5
areturn
6
aload_1
7
areturn



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


    void setIt(int value) {
    	i = value;
    }
    int getIt() {
    	return i;
    }

стать

Метод void setIt(int)

0
aload_0
1
iload_1
2
putfield #4
// Field Example.i I
5
return



    Method int getIt()

0
aload_0
1
getfield #4
// Field Example.i I
4
ireturn


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


7.9 Массивы

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


    void createBuffer() {
    	int buffer[];
    	int bufsz = 100;
    	int value = 12;
    	buffer = new int[bufsz];
    	buffer[10] = value;
    	value = buffer[11];
    }

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

Метод void createBuffer()

0
bipush 100
// Push bufsz
2
istore_2
// Store bufsz in local 2
3
bipush 12
// Push value
5
istore_3
// Store value in local 3
6
iload_2
// Push bufsz...
7
newarray int
// ...and create new array of int
9
astore_1
// Store new array in buffer
10
aload_1
// Push buffer
11
bipush 10
// Push constant 10
13
iload_3
// Push value
14
iastore
// Store value at buffer[10]
15
aload_1
// Push buffer
16
bipush 11
// Push constant 11
18
iaload
// Push value at buffer[11]
19
istore_3
// ...and store it in value
20
return


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


    void createThreadArray() {
    	Thread threads[];
    	int count = 10;
    	threads = new Thread[count];
    	threads[0] = new Thread();
    }

становится

Метод void createThreadArray()

0
bipush 10
// Push 10...
2
istore_2
// ...and initialize count to that
3
iload_2
// Push count, used by anewarray
4
anewarray class #1
// Create new array of class Thread
7
astore_1
// Store new array in threads
8
aload_1
// Load value of threads on stack
9
iconst_0
// Load 0 into stack
10
new #1
// Create instance of class Thread
13
dup
// Make duplicate reference...
14
invokespecial #5
// ...to pass to initialization method


// Method java.lang.Thread.<init>()V
17
aastore
// Store new Thread in array at 0
18
return


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


    int[][][] create3DArray() {
    	int grid[][][];
    	grid = new int[10][5][];
    	return grid;
    }

создается

Метод int create3DArray()[][][]

0
bipush 10
// Push 10 (dimension one)
2
iconst_5
// Push 5 (dimension two)
3
multianewarra y #1 dim #2
// Class [[[I, a three


// dimensional int array;


// only create first two


// dimensions
7
astore_1
// Store new array...
8
aload_1
// ...then prepare to return it
9
areturn


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

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


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

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


    int chooseNear(int i) {int chooseNear(int i) {
    	switch (i) {
    	    case 0: 			return 0;
    	    case 1: 			return 1;
    	    case 2: 			return 2;
    	    default:			return -1;
    	}
    }


    
    
компиляции к

Метод int chooseNear(int)

0
iload_1
// Load local 1 (argument i)
1
tableswitch 0 to 2:
// Valid indices are 0 through 2

0: 28
// If i is 0, continue at 28

1: 30
// If i is 1, continue at 30

2: 32
// If i is 2, continue at 32

default:34
// Otherwise, continue at 34
28
iconst_0
// i was 0; push int 0...
29
ireturn
// ...and return it
30
iconst_1
// i was 1; push int 1...
31
ireturn
// ...and return it
32
iconst_2
// i was 2; push int 2...
33
ireturn
// ...and return it
34
iconst_m1
// otherwise push int -1...
35
ireturn
// ...and return it


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 цель. Например, скомпилированный код для

int chooseFar(int i) {


switch (i) {

case -100:
return -1;

case 0:
return 0;

case 100:
return 1;

default:
return -1;

}

}



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

Метод int chooseFar(int)

0
iload_1
1
lookupswitch 3:


-100: 36


0: 38


100: 40


default:42
36
iconst_m1
37
ireturn
38
iconst_0
39
ireturn
40
iconst_1
41
ireturn
42
const_m1
43
ireturn


T

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


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

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


    public long nextIndex() {
    	return index++;
    }
    private long index = 0;

компилируется в

Метод long nextIndex()

0
aload_0
// Write this onto operand stack
1
dup
// Make a copy of it
2
getfield #4
// One of the copies of this is consumed


// loading long field index onto stack,


// above the original this
5
dup2_x1
// The long on top of the stack is


// inserted into the stack below the


// original this
6
lconst_1
// A long 1 is loaded onto the stack
7
ladd
// The index value is incremented
8
putfield #4
// and the result stored back in the field
11
lreturn
// The original value of index is left on


// top of the stack, ready to be returned


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


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

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


    void cantBeZero(int i) throws TestExc {
    	if (i == 0) {
    	    throw new TestExc();
    	}
    }

становится

Метод void cantBeZero(int)

0
iload_1
// Load argument 1 (i) onto stack
1
ifne 12
// If i==0, allocate instance and throw
4
new #1
// Create instance of TestExc
7
dup
// One reference goes to the constructor
8
invokespecial #7
// Method TestExc.<init>()V
11
athrow
// Second reference is thrown
12
return
// Never get here if we threw TestExc


Компиляция Java try-catch является прямым. Например:


    void catchOne() {
    	try {
    	    tryItOut();
    	} catch (TestExc e) {
    	    handleExc(e);
    	}

    }
компилируется как

Метод void catchOne()

0
aload_0
// Beginning of try block
1
invokevirtual #6
// Method Example.tryItOut()V
4
return
// End of try block; normal return
5
astore_1
// Store thrown value in local variable 1
6
aload_0
// Load this onto stack
7
aload_1
// Load thrown value onto stack
8
invokevirtual #5
// Invoke handler method:


// Example.handleExc(LTestExc;)V
11
return
// Return after handling TestExc


Таблица исключений:

От К Цель Ввести
0 4 5 Класс TestExc


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

Метод void catchOne()

0
aload_0
// Beginning of try block
1
invokevirtual #4
// Method Example.tryItOut()V
4
return
// End of try block; normal return


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

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

5
astore_1
//Сохраните брошенное значение в локальной переменной 1
6
aload_0
//Загрузка this на стек
7
aload_1
//Загрузите брошенное значение на стек
8
invokevirtual #5
//Вызовите метод обработчика:


// Example.handleExc(LTestExc;)V
11
возвратиться
//Возвратитесь после обработки TestExc


    Exception table: 

From   To   Target   Type  
0   4   5   Class TestExc  

    The invocation of handleExc, the contents of the catch clause, is also compiled like a normal method invocation. However, the presence of a catch clause causes the compiler to generate an exception table entry. The exception table for the catchOne method has one entry corresponding to the one argument (an instance of class TestExc) that the catch clause of catchOne can handle. If some value that is an instance of TestExc is thrown during execution of the instructions between in-dices 0 and 4 (inclusive) in catchOne, control is transferred to the Java Virtual Machine code at index 5, which implements the block of the catch clause. If the value that is thrown is not an instance of TestExc, the catch clause of catchOne cannot handle it. Instead, the value is rethrown to the invoker of catchOne.

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


    void catchTwo() {void catchTwo() {
    	try {
    	    tryItOut();
    	} catch (TestExc1 e) {
    	    handleExc(e);
    	} catch (TestExc2 e) {
    	    handleExc(e);
    	}
    
Многократный catch пункты данного try оператор компилируется, просто добавляя код виртуальной машины Java для каждого catch пункт один за другим, и добавляющие записи в таблицу исключений:

Метод void catchTwo()}

0
aload_0
// Begin try block
1
invokevirtual #5
// Method Example.tryItOut()V
4
return
// End of try block; normal return
5
astore_1
// Beginning of handler for TestExc1;


// Store thrown value in local variable 1
6
aload_0
// Load this onto stack
7
aload_1
// Load thrown value onto stack
8
invokevirtual #7
// Invoke handler method:


// Example.handleExc(LTestExc1;)V
11
return
// Return after handling TestExc1
12
astore_1
// Beginning of handler for TestExc2;


// Store thrown value in local variable 1
13
aload_0
// Load this onto stack
14
aload_1
// Load thrown value onto stack
15
invokevirtual #7
// Invoke handler method:


// Example.handleExc(LTestExc2;)V
18
return
// Return after handling TestExc2


    Exception table:

From To Target Type
0 4
5
Class TestExc1
0 4
12
Class TestExc2


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

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


    void nestedCatch() {
    void nestedCatch() {
    	try {
    	    try {
    	        tryItOut();
    	    } catch (TestExc1 e) {
    			handleExc1(e);
    	    }
    	} catch (TestExc2 e) {
    	    handleExc2(e);
    	}
    }

становится

Метод void nestedCatch()

0
aload_0
// Begin try block
1
invokevirtual #8
// Method Example.tryItOut()V
4
return
// End of try block; normal return
5
astore_1
// Beginning of handler for TestExc1;


// Store thrown value in local variable 1
6
aload_0
// Load this onto stack
7
aload_1
// Load thrown value onto stack
8
invokevirtual #7
// Invoke handler method:


// Example.handleExc1(LTestExc1;)V
11
return
// Return after handling TestExc1
12
astore_1
// Beginning of handler for TestExc2;


// Store thrown value in local variable 1
13
aload_0
// Load this onto stack
14
aload_1
// Load thrown value onto stack
15
invokevirtual #6
// Invoke handler method:


// Example.handleExc2(LTestExc2;)V
18
return
// Return after handling TestExc2


Таблица исключений:

От
К
Цель
Ввести
0
4
5
Класс TestExc1
0
12
12
Класс TestExc2


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

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


7.13 Компиляции наконец

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

void tryFinally() {


try {

tryItOut();

} finally {

wrapItUp();

}
}



скомпилированный код

Метод void tryFinally()

0
aload_0
// Beginning of try block
1
invokevirtual #6
// Method Example.tryItOut()V
4
jsr 14
// Call finally block
7
return
// End of try block
8
astore_1
// Beginning of handler for any throw
9
jsr 14
// Call finally block
12
aload_1
// Push thrown value,
13
athrow
// and rethrow the value to the invoker
14
astore_2
// Beginning of finally block
15
aload_0
// Push this onto stack
16
invokevirtual #5
// Method Example.wrapItUp()V
19
ret 2
// Return from finally block


Таблица исключений:

От
К
Цель
Ввести
0
4
8
любой


Есть четыре пути к управлению, чтобы передать за пределами 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 пункт более сложен:

void tryCatchFinally() {

   try {

       tryItOut();

   } catch (TestExc e) {

       handleExc(e);

   } finally {

       wrapItUp();

   }

}



becomes

Method void tryCatchFinally()

0
 
aload_0
 
// Beginning of try block  
1
 
invokevirtual #4
 
// Method Example.tryItOut()V  
4
 
goto 16
 
// Jump to finally block  
7
 
astore_3
 
// Beginning of handler for TestExc;  

 

 
// Store thrown value in local variable 3  
8
 
aload_0
 
// Push this onto stack  
9
 
aload_3
 
// Push thrown value onto stack  
10
 
invokevirtual #6
 
// Invoke handler method:  

 

 
// Example.handleExc(LTestExc;)V  
13
 
goto 16
 
// Huh???1  
16
 
jsr 26
 
// Call finally block  
19
 
return
 
// Return after handling TestExc  
20
 
astore_1
 
// Beginning of handler for exceptions  

 

 
// other than TestExc, or exceptions  

 

 
// thrown while handling TestExc  
21
 
jsr 26
 
// Call finally block  
24
 
aload_1
 
// Push thrown value,  
25
 
athrow
 
// and rethrow the value to the invoker  
26
 
astore_2
 
// Beginning of finally block  
27
 
aload_0
 
// Push this onto stack  
28
 
invokevirtual #5
 
// Method Example.wrapItUp()V  

31
ret 2
// Return from finally block


    
    Exception table:

From To Target Type
0 4 7 Class TestExc
0 16 20 any


    

If the try statement completes normally, the goto instruction at index 4 jumps to the subroutine call for the finally block at index 16. The finally block at index 26 is executed, control returns to the return instruction at index 19, and tryCatchFinally returns normally.

If tryItOut throws an instance of TestExc, the first (innermost) applicable exception handler in the exception table is chosen to handle the exception. The code for that exception handler, beginning at index 7, passes the thrown value to handleExc, and on its return makes the same subroutine call to the finally block at index 26 as in the normal case. If an exception is not thrown by handleExc, tryCatchFinally returns normally.

If tryItOut throws a value that is not an instance of TestExc, or if handleExc itself throws an exception, the condition is handled by the second entry in the exception table, which handles any value thrown between indices 0 and 16. That exception handler transfers control to index 20, where the thrown value is first stored in local variable 1. The code for the finally block at index 26 is called as a subroutine. If it returns, the thrown value is retrieved from local variable 1 and rethrown using the athrow instruction. If a new value is thrown during execution of the finally clause, the finally clause aborts and tryCatchFinally returns abnormally, throwing the new value to its invoker.


7.14 Synchronization

The Java Virtual Machine provides explicit support for synchronization through its monitorenter and monitorexit instructions. For Java, however, perhaps the most common form of synchronization is the synchronized method.

A synchronized method is not normally implemented using monitorenter and monitorexit. Rather, it is simply distinguished in the constant pool by the ACC_SYNCHRONIZED flag, which is checked by the method invocation instructions. When invoking a method for which ACC_SYNCHRONIZED is set, the current thread acquires a monitor, invokes the method itself, and releases the monitor whether the method invocation completes normally or abruptly. During the time the executing thread owns the monitor, no other thread may acquire it. If an exception is thrown during invocation of the synchronized method, and the synchronized method does not handle the exception, the monitor for the method is automatically released before the exception is rethrown out of the synchronized method.

The monitorenter and monitorexit instructions exist to support Java's synchronized statements. A synchronized statement acquires a monitor on behalf of the executing thread, executes the body of the statement, then releases the monitor:


    void onlyMe(Foo f) {
    	synchronized(f) {
    	    doSomething();
    	}
    }

Compilation of synchronized statements is straightforward:

Method void onlyMe(Foo)

0
aload_1
// Load f onto operand stack
1
astore_2
// Store it in local variable 2
2
aload_2
// Load local variable 2 (f) onto stack
3
monitorenter
// Enter the monitor associated with f
4
aload_0
// Holding the monitor, pass this and
5
invokevirtual #5
// call Example.doSomething()V
8
aload_2
// Load local variable 2 (f) onto stack
9
monitorexit
// Exit the monitor associated with f
10
return
// Return normally
11
aload_2
// In case of any throw, end up here
12
monitorexit
// Be sure to exit monitor,
13
athrow
// then rethrow the value to the invoker


Exception table:

From To Target a Type
4 8 11 any



Содержание | Предыдущий | Следующий | Индекс

Java Virtual Machine Specification

Copyright © 1996, 1997 Sun Microsystems, Inc. All rights reserved
Please send any comments or corrections to jvm@java.sun.com

free hit counter