Содержание | Предыдущий | Следующий | ИндексСпецификация Виртуальной машины JavaTM


ГЛАВА 7

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

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

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

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

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

  10   ldc #1 			// Push float constant 100.0	
или

   9   invokevirtual #4		// Method Example.addTwo(II)I
В целях этой главы мы не волнуемся об определении деталей, таких как размеры операнда.


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

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

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

void spin() {
    int i;
    for (i = 0; i < 100; i++) {
     	;			// Loop body is empty
    }
}
Компилятор мог бы скомпилировать spin к

Method void spin()
   0	iconst_0		// Push int constant 0
   1 	istore_1		// Store into local variable 1 (i=0)
   2	goto 8			// First time through don't increment
   5	iinc 1 1		// Increment local variable 1 by 1 (i++)
   8	iload_1			// Push local variable 1 (i)
   9	bipush 100		// Push int constant 100
  11	if_icmplt 5		// Compare and loop if less than (i < 100)
  14	возвратитесь			//Возврат void когда сделано
Виртуальная машина 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 выполняется, главным образом, этими инструкциями:

   5	iinc 1 1		// Increment local 1 by 1 (i++)
   8	iload_1			// Push local variable 1 (i)
   9	bipush 100		// Push int constant 100
  11	if_icmplt 5		// Compare and loop if less than (i < 100)
bipush инструкция продвигает значение 100 на стек операнда как int, тогда if_icmplt инструкция выталкивает то значение от стека операнда и сравнивает это со мной. Если сравнение успешно выполняется (переменная 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
    }
}
скомпилированный код

Method void dspin()
   0 	dconst_0		// Push double constant 0.0
   1 	dstore_1		// Store into local variables 1 and 2
   2 	goto 9			// First time through don't increment
   5 	dload_1			// Push local variables 1 and 2 
   6 	dconst_1		// Push double constant 1.0 
   7 	dadd			// Add; there is no dinc instruction
   8 	dstore_1		// Store result in local variables 1 and 2
   9 	dload_1			// Push local variables 1 and 2 
  10 	ldc2_w #4 		// Push double constant 100.0 
  13 	dcmpg			// There is no if_dcmplt instruction
  14 	iflt 5			// Compare and loop if less than (i < 100.0)
  17	возвратитесь			//Возврат void когда сделано
Инструкции, которые работают на введенных данных, теперь специализируются для типа double. (ldc2_w инструкция будет обсуждена позже в этой главе.)

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

double doubleLocals(double d1, double d2) {
    return d1 + d2;
}
становится

Method double doubleLocals(double,double)
   0 	dload_1			// First argument in local variables 1 and 2
   1 	dload_3			// Second argument in local variables 3 and 4
   2 	dadd			
   3 	dreturn
Отметьте что локальные переменные пар локальной переменной, используемых, чтобы сохранить 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:

void sspin() {
    short i;
    for (i = 0; i < 100; i++) {
        ;			// Loop body is empty
    }
}
Это должно быть скомпилировано для виртуальной машины Java, следующим образом, используя инструкции, работающие на другом типе, наиболее вероятно int, преобразование между short и int значения по мере необходимости, чтобы гарантировать, что результаты операций на short данные остаются в пределах соответствующего диапазона:

Method void sspin()
   0 	iconst_0
   1 	istore_1
   2 	goto 10
   5 	iload_1			// The short is treated as though 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			// Push grain 
   6 	iconst_1		// Push int constant 1 
   7 	isub			// Subtract; push result 
   8 	iconst_m1		// Push int constant -1 
   9 	ixor			// Do XOR; push result 
Сначала grain - 1 вычисляется, используя содержание локальной переменной 2 и непосредственное int значение 1. Эти операнды выталкиваются от стека операнда и их различия, пододвинутого обратно на стек операнда. Различие таким образом сразу доступно для использования в качестве одного операнда ixor инструкции. (Вспомните это ~x == -1^x.) Точно так же результат ixor инструкции становится операндом для последующей iand инструкции.

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

Method 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). Данные типов 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> инструкции.

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

void useManyNumeric() {
    int i = 100;
    int j = 1000000;
    long l1 = 1;
    long l2 = 0xffffffff;
    double d = 2.2;
    ...do some calculations...
}
устанавливаются следующим образом:

Method void useManyNumeric()
   0	bipush 100		// Push a small int with bipush
   2	istore_1
   3 	ldc #1 			// Push int constant 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 		// Push long 0xffffffff (that is, an int -1); any
				// long constant value can be pushed using ldc2_w
  11	lstore 5
  13 	ldc2_w #8 		// Push double constant 2.200000; uncommon
				// double values are also pushed using ldc2_w
  16 	dstore 7
...do those calculations...

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, например:

void whileInt() {
    int i = 0;
    while (i < 100) {
        i++;
    }
}
компилируется в

Method 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++;
    }
}
компилируется в

Method 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 		// Push double constant 100.1
  13 	dcmpg			// To do the compare and branch 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;				
    }
}
компиляции к

Method int lessThan100(double)
   0 	dload_1
   1 	ldc2_w #4 		// Push double constant 100.0
   4 	dcmpg			// Push 1 if d is NaN or d \> 100.0;
				// push 0 if d == 100.0
   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;			
    }
}
становится

Method int greaterThan100(double)
   0 	dload_1
   1 	ldc2_w #4 		// Push double constant 100.0
   4 	dcmpl			// Push -1 if d is Nan or d < 100.0;
				//продвиньте 0 если d == 100.0
   5	ifle 10			//Ответвление по 0 или-1
   8	iconst_1
   9	ireturn
  10	iconst_m1
  11	ireturn
Еще раз, ли сравнение перестало работать на значении NON-НЭН или потому что его передают НЭН, dcmpl инструкция продвигает int значение на стек операнда, который заставляет ifle переходить. Если бы обе из dcmp инструкций не существовали, один из методов в качестве примера должен был бы сделать больше работы, чтобы обнаружить НЭН.


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

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

int addTwo(int i, int j) {
    return i + j;
}
компиляции к

Method int addTwo(int,int)
   0	iload_1			// Push value of local variable 1 (i)
   1	iload_2			// Push value of local variable 2 (j)
   2	iadd			// Add; leave int result on operand stack
   3 	ireturn			// Return int result
Условно, метод экземпляра передают a reference к его экземпляру в локальной переменной 0. В языке программирования Java экземпляр доступен через this ключевое слово.

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

static int addTwoStatic(int i, int j) {
    return i + j;
}
компиляции к

Method int addTwoStatic(int,int)
   0 	iload_0
   1	iload_1
   2 	iadd
   3 	ireturn
Единственная разница - то, что параметры метода кажутся запускающимися в локальной переменной 0, а не 1.


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

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

int add12and13() {
    return addTwo(12, 13);
}
Это компилирует в

Method int add12and13()
   0 	aload_0			 	// Push local variable 0 (this)
   1 	bipush 12			// Push int constant 12
   3 	bipush 13			// Push int constant 13
   5 	invokevirtual #4		// Method Example.addtwo(II)I
   8	ireturn			 	// Return int on top of operand stack; it is
				 	// the int result of 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, подобно, как показано:

int add12and13() {
    return addTwoStatic(12, 13);
}
хотя различная инструкция вызова метода виртуальной машины Java используется:

Method 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 инструкция должна использоваться, чтобы вызвать методы инициализации экземпляра (см. Раздел 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 метод), становится

Method int getItNear()
   0 	aload_0
   1 	invokespecial #5 		// Method Near.getIt()I
   4 	ireturn
Метод Far.getItFar (который вызывает метод суперкласса), становится

Method int getItFar()
   0 	aload_0
   1 	invokespecial #4		// Method Near.getItNear()I
   4	ireturn
Отметьте, что методы вызывали использование invokespecial инструкции, всегда передают this к вызванному методу как его первый параметр. Как обычно это получается в локальной переменной 0.


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

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

Object create() {
    return new Object();
}
компиляции к

Method 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;
    }
}
становится

Method 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
Method 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;
}
стать

Method 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) не являются смещениями полей в экземпляре класса. Компилятор генерирует символьные ссылки на поля экземпляра, которые сохранены в пуле константы этапа выполнения. Те элементы пула константы этапа выполнения разрешаются во время выполнения, чтобы определить расположение поля в пределах объекта, на который ссылаются.


7.9 Массивы

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

void createBuffer() {
    int buffer[];
    int bufsz = 100;
    int value = 12;
    buffer = new int[bufsz];
    buffer[10] = value;
    value = buffer[11];
}
мог бы быть скомпилирован в

Method void createBuffer()
   0 	bipush 100		// Push int constant 100 (bufsz)
   2 	istore_2		// Store bufsz in local variable 2
   3 	bipush 12		// Push int constant 12 (value)
   5 	istore_3		// Store value in local variable 3
   6	iload_2			// Push bufsz...
   7	newarray int		// ...and create new array of int of that length
   9 	astore_1		// Store new array in buffer
  10 	aload_1			// Push buffer
  11 	bipush 10		// Push int constant 10
  13 	iload_3			// Push value
  14 	iastore			// Store value at buffer[10]
  15 	aload_1			// Push buffer
  16 	bipush 11		// Push int 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();
}
становится

Method void createThreadArray()
   0 	bipush 10			// Push int constant 10
   2 	istore_2			// 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				// Push value of threads
   9 	iconst_0			// Push int constant 0
  10 	new #1 				// Create instance of class Thread
  13 	dup				// Make duplicate reference...
  14 	invokespecial #5 		// ...to pass to instance 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;
}
создается

Method int create3DArray()[][][]
   0 	bipush 10			// Push int 10 (dimension one)
   2 	iconst_5			// Push int 5 (dimension two)
   3 	multianewarray #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 Переключателей Компиляции

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

int chooseNear(int i) {
    switch (i) {
        case 0:  return 0;
        case 1:  return 1;
        case 2:  return 2;
        default: return -1;
    }
}
компиляции к

Method int chooseNear(int)
   0	iload_1				// Push local variable 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 constant 0...
  29 	ireturn				// ...and return it
  30 	iconst_1			// i was 1; push int constant 1...
  31 	ireturn				// ...and return it
  32 	iconst_2			// i was 2; push int constant 2...
  33 	ireturn				// ...and return it
  34 	iconst_m1			// otherwise push int constant -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 инструкции:

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


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

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

public long nextIndex() { 
    return index++;
}
private long index = 0;
компилируется в

Method long nextIndex()
   0 	aload_0			// Push this
   1 	dup			// Make a copy of it
   2 	getfield #4 		// One of the copies of this is consumed
				// pushing long field index,
				// above the original this
   5 	dup2_x1			// The long on top of the operand stack is 
				// inserted into the operand stack below the 
				// original this
   6 	lconst_1		// Push long constant 1 
   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 operand stack, ready to be returned
Отметьте, что виртуальная машина Java никогда не позволяет ее инструкциям манипулирования стеком операнда изменять или разбивать отдельные значения на стеке операнда.


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

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

void cantBeZero(int i) throws TestExc {
    if (i == 0) {
        throw new TestExc();
    }
}
становится

Method void cantBeZero(int)
   0 	iload_1				// Push argument 1 (i)
   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
Компиляция try-catch конструкции являются прямыми. Например,

void catchOne() {
    try {
        tryItOut();
    } catch (TestExc e) {
        handleExc(e);
    }
}
компилируется как

Method 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				// Push this
   7 	aload_1				// Push thrown value
   8 	invokevirtual #5 		// Invoke handler method: 
					// Example.handleExc(LTestExc;)V
  11 	return				// Return after handling TestExc
Exception table:
   	From 	To 	Target 		Type
     	0     	4     	5 		Class TestExc
Смотря более близко, try блок компилируется, как это было бы если try не присутствовали:

Method 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			// Store thrown value in local variable 1
   6 	aload_0				// Push this 
   7 	aload_1				// Push thrown value
   8 	invokevirtual #5 		// Invoke handler method: 
					// Example.handleExc(LTestExc;)V
  11 	return				// Return after handling TestExc
Exception table:
   	From 	To 	Target 		Type
     	0     	4     	5 		Class TestExc
Вызов handleExc, содержание catch пункт, также компилируется как нормальный вызов метода. Однако, присутствие a catch пункт заставляет компилятор генерировать запись таблицы исключений. Таблица исключений для catchOne у метода есть одна запись, соответствующая одному параметру (экземпляр класса TestExc) то, что catch пункт catchOne может обработать. Если некоторое значение, которое является экземпляром TestExc бросается во время выполнения инструкций между индексами 0 и 4 в catchOne, управление передается коду виртуальной машины Java по индексу 5, который реализует блок catch пункт. Если значение, которое бросается, не является экземпляром TestExc, catch пункт catchOne не может обработать это. Вместо этого значение повторно бросается в invoker catchOne.

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

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

Method 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				// Push this
   7 	aload_1				// Push thrown value
   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				// Push this
  14 	aload_1				// Push thrown value
  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() {
    try {
        try {
            tryItOut();
        } catch (TestExc1 e) {
            handleExc1(e);
        }
    } catch (TestExc2 e) {
        handleExc2(e);
    }
}
становится

Method 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				// Push this
   7 	aload_1				// Push thrown value
   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				// Push this
  14 	aload_1				// Push thrown value
  15 	invokevirtual #6 		// Invoke handler method:
					// Example.handleExc2(LTestExc2;)V
  18 	return				// Return after handling TestExc2
Exception table:
   	From 	To 	Target 		Type
     	0     	4     	5 		Class TestExc1
     	0    	12    	12 		Class TestExc2
Вложение 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 пункт должен сначала быть выполнен. Для этого простого примера

void tryFinally() {
    try {
        tryItOut();
    } finally {
        wrapItUp();
    }
}
скомпилированный код

Method 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
  16 	invokevirtual #5 		// Method Example.wrapItUp()V
  19 	ret 2				// Return from finally block
Exception table:
   	From 	To 	Target 		Type
	0    	4    	8   		any
Есть четыре пути к управлению, чтобы передать за пределами 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();
    }
}
становится

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
   9 	aload_3				// Push thrown value
  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
  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
Если 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 операторы. Например:

void onlyMe(Foo f) {
    synchronized(f) {
        doSomething();
    }
}
компилируется в

Method void onlyMe(Foo)
   0 	aload_1				// Push f	
   1 	astore_2			// Store it in local variable 2
   2 	aload_2				// Push local variable 2 (f)
   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				// Push local variable 2 (f)
   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 		Type
     	4     	8    	11   		any

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



Spec-Zone.ru - all specs in one place



free hit counter