Spec-Zone .ru
спецификации, руководства, описания, API
Содержание документации

Поддержка Виртуальной машины Java Языков не-Java

Следующие темы затрагиваются:

Введение

Java платформа SE включает разработке приложений со следующими функциями:

Java платформа SE также оказывает устойчивую поддержку относительно следующих областей (и больше):

JVM HotSpot Oracle также предлагает следующие инструменты и функции:

Java SE 7 языков не-Java включений платформы, чтобы использовать инфраструктуру и потенциальную оптимизацию производительности JVM. Ключевой механизм invokedynamic инструкция, которая упрощает реализацию компиляторов и систем времени выполнения для динамически типизированных языков на JVM.

Статический контроль типов и Динамический контроль типов

Язык программирования со статическим контролем типов, если он выполняет тип, проверяющий во время компиляции. Проверка типа является процессом проверки, что программа безопасна с точки зрения типов. Программа безопасна с точки зрения типов, если параметрами всех ее операций является корректный тип.

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

Следующий пример Привет Мировой программы демонстрирует статический контроль типов. Типы показывают полужирным.

import java.util.Date;

public class HelloWorld {
    public static void main(String[] argv) {
        String hello = "Hello ";
        Date currDate = new Date();
        for (String a : argv) {
            System.out.println(hello + a);
            System.out.println("Today's date is: " + currDate);
        }
    }
}

Язык программирования с динамическим контролем типов, если он выполняет проверку типа во время выполнения. JavaScript и Ruby являются примерами динамически типизированных языков. Эти языки проверяют во время выполнения, а не во время компиляции, который оценивает в приложении, соответствуют ожидаемым типам. Эти языки обычно не имеют информации о типе в наличии во время компиляции. Тип объекта может быть определен только во время выполнения. Следовательно, в прошлом было трудно эффективно реализовать их на JVM.

Следующее является примером Привет Мировой программы, записанной в языке программирования Ruby:

#!/usr/bin/env ruby
require 'date'

hello = "Hello "
currDate = DateTime.now
ARGV.each do|a|
  puts hello + a
  puts "Date and time: " + currDate.to_s
end

Отметьте, что каждое имя представляется без описания типа. Кроме того, основная программа не располагается в типе держателя (класс Java HelloWorld. Ruby, эквивалентный из Java for цикл в динамическом типе переменной ARGV. Тело цикла содержится в блоке, названном закрытием, типичной функцией на динамических языках.

Статически типизированные языки Являются Не Обязательно Языками Со строгим контролем типов

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

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

a = "40"
b = a + 2

В этом примере язык программирования Ruby не будет неявно бросать номер 2, у которого есть a Fixnum введите к строке.

Проблема Компиляции Динамически типизированных языков

Рассмотрите следующий метод с динамическим контролем типов, addtwo, это добавляет любые два числа (который может иметь любой числовой тип), и возвращает сумму:

def addtwo(a, b)
       a + b;
end

Предположите, что Ваша организация реализует компилятор и систему времени выполнения для языка программирования в который метод addtwo пишется. На языке со строгим контролем типов, введенный ли статически или динамически, поведение + (оператор сложения), зависит от типов операндов. Компилятор для статически типизированного языка выбирает который реализация + соответствующее основанный на статических типах a и b. Например, компилятор Java реализует + с iadd Инструкция JVM, если типы a и b int. Оператор сложения будет скомпилирован в вызов метода потому что JVM iadd инструкция требует, чтобы типы операнда были статически известны.

Напротив, компилятор для динамически типизированного языка должен задержать выбор до времени выполнения. Оператор a + b компилируется как вызов метода +(a, b), где + имя метода. (Отметьте названный метод + разрешается в JVM, но не в языке программирования Java.) Предполагают тогда, что система времени выполнения для динамически типизированного языка в состоянии идентифицировать это a и b переменные целочисленного типа. Система времени выполнения предпочла бы вызывать реализацию + это специализируется для целочисленных типов, а не произвольных объектных типов.

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

Java SE 7 представляет invokedynamic инструкция, которая позволяет системе времени выполнения настроить редактирование между сайтом вызова и реализацией метода. В этом примере, invokedynamic сайт вызова +. invokedynamic сайт вызова соединяется с методом посредством метода начальной загрузки, который является методом, определенным компилятором для динамически типизированного языка, однажды который вызывает JVM, чтобы соединить сайт. Принятие компилятора, испускаемого invokedynamic инструкция, которая вызывает +, и предполагая, что система времени выполнения знает о методе adder(Integer,Integer), время выполнения может соединиться invokedynamic вызовите сайт к adder метод следующим образом:

IntegerOps.java

class IntegerOps {

  public static Integer adder(Integer x, Integer y) {
    return x + y;
  }
}

Example.java

import java.util.*;
import java.lang.invoke.*;
import static java.lang.invoke.MethodType.*;
import static java.lang.invoke.MethodHandles.*;

class Example {

  public static CallSite mybsm(
    MethodHandles.Lookup callerClass, String dynMethodName, MethodType dynMethodType)
    throws Throwable {

    MethodHandle mh =
      callerClass.findStatic(
        Example.class,
        "IntegerOps.adder",
        MethodType.methodType(Integer.class, Integer.class, Integer.class));

    if (!dynMethodType.equals(mh.type())) {
      mh = mh.asType(dynMethodType);
    }

    return new ConstantCallSite(mh);
  }
}

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

Метод Example.mybsm метод начальной загрузки, который соединяется invokedynamic вызовите сайт к adder метод.

Объект callerClass объект поиска, который является фабрикой для того, чтобы создать дескрипторы метода.

Метод MethodHandles.Lookup.findStatic (вызванный от callerClass объект поиска), создает статический дескриптор метода для метода adder.

Отметьте: Этот метод начальной загрузки соединяется invokedynamic вызовите сайт только к коду, определенному в adder метод, и это предполагает что параметры, данные invokedynamic сайт вызова будет Integer объекты. Метод начальной загрузки требует, чтобы дополнительный код должным образом соединился invokedynamic вызовите сайты к соответствующему коду, чтобы выполниться если параметры метода начальной загрузки (в этом примере, callerClass, dynMethodName, и dynMethodType) измениться.

Классы java.lang.invoke.MethodHandles и java.lang.invoke.MethodHandle содержите различные методы, которые создают дескрипторы метода, основанные на существующих дескрипторах метода. Этот пример вызывает метод asType если тип метода дескриптора метода mh не соответствует тип метода, определенный параметром dynMethodType. Это позволяет методу начальной загрузки соединиться invokedynamic вызовите сайты к методам Java, типы метода которых точно не соответствуют.

ConstantCallSite экземпляр, возвращенный методом начальной загрузки, представляет сайт вызова, который будет связан с отличным invokedynamic инструкция. Цель для a ConstantCallSite экземпляр является постоянным и никогда не может изменяться. В этом случае есть только один метод Java, adder, который является кандидатом на выполнение сайта вызова. Отметьте, что этот метод не должен быть методом Java. Вместо этого если были несколько таких методов, являющихся доступным системе времени выполнения, каждый обрабатывающий различный параметр типы, метод начальной загрузки mybsm мог динамически выбрать корректный метод, основанный на dynMethodType параметр.

invokedynamic Инструкция

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

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

invokedynamic инструкция содержит постоянный индекс пула (в том же самом формате что касается другого invoke инструкции). Этот постоянный пул индексирует ссылки a CONSTANT_InvokeDynamic запись. Эта запись определяет метод начальной загрузки (a CONSTANT_MethodHandle запись), имя динамически соединенного метода, и типы параметра и тип возврата звонка в динамически соединенный метод.

Следующее является примером invokedynamic инструкция. В этом примере система времени выполнения соединяет динамический сайт вызова, определенный этим invokedynamic инструкция (который является +, оператор сложения) к IntegerOps.adder метод при использовании метода начальной загрузки Example.mybsm. Методы adder и mybsm определяются в разделе Проблема Компиляции Динамически типизированных языков (разрывы строки были добавлены для ясности):

invokedynamic   InvokeDynamic
  REF_invokeStatic:
    Example.mybsm:
      "(Ljava/lang/invoke/MethodHandles/Lookup;
        Ljava/lang/String;
        Ljava/lang/invoke/MethodType;)
      Ljava/lang/invoke/CallSite;":
    +:
      "(Ljava/lang/Integer;
        Ljava/lang/Integer;)
      Ljava/lang/Integer;";

Отметьте: примеры байт-кода в этих разделах используют синтаксис платформы манипулирования и анализа Байт-кода Java ASM.

Вызов динамически соединенного метода с invokedynamic инструкция включает следующие шаги:

  1. Определение Метода Начальной загрузки
  2. Определение Постоянных Записей Пула
  3. Используя invokedynamic Инструкция

1. Определение Метода Начальной загрузки

Во время выполнения, когда JVM сначала встречается invokedynamic инструкция, это вызывает метод начальной загрузки. Этот метод соединяет имя, определенное invokedynamic инструкция с кодом, который должен быть выполнен (целевой метод), на который ссылается дескриптор метода. Если JVM выполняет то же самое invokedynamic инструкция снова, это не вызывает метод начальной загрузки; это автоматически вызывает соединенный дескриптор метода.

Тип возврата метода начальной загрузки должен быть java.lang.invoke.CallSite. A CallSite объект представляет соединенное состояние invokedynamic инструкция и метод обрабатывают, с которым это соединяется.

Метод начальной загрузки берет три или больше параметра:

  1. A MethodHandles.Lookup объект, который является фабрикой для того, чтобы создать дескрипторы метода в контексте invokedynamic инструкция.
  2. A String объект, имя метода упоминается в динамическом сайте вызова.
  3. A MethodType объект, разрешенная подпись типа динамического сайта вызова.
  4. Дополнительно, один или более дополнительных статических параметров invokedynamic инструкция. Эти параметры, оттянутые из постоянного пула, предназначаются, чтобы помочь разработчикам языка безопасно и сжато закодировать дополнительные метаданные, полезные для метода начальной загрузки. В принципе имя и дополнительные параметры избыточны, так как каждому сайту вызова можно было дать его собственный уникальный метод начальной загрузки. Однако, такая практика, вероятно, произведет большие файлы класса и постоянные пулы

См. раздел Проблема Компиляции Динамически типизированных языков для примера метода начальной загрузки.

2. Определение Постоянных Записей Пула

Как упомянуто ранее, invokedynamic инструкция содержит ссылку на запись в постоянном пуле с тегом CONSTANT_InvokeDynamic. Эта запись содержит ссылки на другие записи в постоянном пуле и ссылки на атрибуты. Этот раздел кратко описывает постоянные записи пула, используемые invokedynamic инструкция. Для получения дополнительной информации см. java.lang.invoke документацию пакета и Спецификацию виртуальной машины Java.

Пример Постоянный Пул

Следующее является выборкой от постоянного пула для класса Example, который содержит метод начальной загрузки Example.mybsm это соединяет метод + с методом Java adder:

    class #159; // #47
    Utf8 "adder"; // #83
    Utf8 "(Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer;"; // #84
    Utf8 "mybsm"; // #87
    Utf8 "(Ljava/lang/invoke/MethodHandles/Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)
      java/lang/invoke/CallSite;"; // #88
    Utf8 "Example"; // #159
    Utf8 "+"; // #166

    // ...

    NameAndType #83 #84; // #228
    Method #47 #228; // #229
    MethodHandle 6b #229; // #230
    NameAndType #87 #88; // #231
    Method #47 #231; // #232
    MethodHandle 6b #232; // #233
    NameAndType #166 #84; // #234
    Utf8 "BootstrapMethods"; // #235
    InvokeDynamic 0s #234; // #236

Постоянная запись пула для invokedynamic инструкция в этом примере содержит три значения:

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

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

  [3] { // Attributes

    // ...

    Attr(#235, 6) { // BootstrapMethods at 0x0F63
      [1] { // bootstrap_methods
        {  //  bootstrap_method
          #233; // bootstrap_method_ref
          [0] { // bootstrap_arguments
          }  //  bootstrap_arguments
        }  //  bootstrap_method
      }
    } // end BootstrapMethods
  } // Attributes

Постоянная запись пула для метода начальной загрузки mybsm дескриптор метода содержит три значения:

Значение 6 подтег REF_invokeStatic. См. следующий раздел, 3. Используя invokedynamic Инструкцию, для получения дополнительной информации об этом подтеге.

3. Используя invokedynamic Инструкцию

Следующий байт-код использует invokedynamic инструкция, чтобы вызвать метод начальной загрузки mybsm, который соединяет динамический сайт вызова (+, оператор сложения) к методу adder. Этот пример использует + метод, чтобы добавить числа 40 и 2 (разрывы строки были вставлены для ясности):

bipush	40;
invokestatic    Method java/lang/Integer.valueOf:"(I)Ljava/lang/Integer;";
iconst_2;
invokestatic    Method java/lang/Integer.valueOf:"(I)Ljava/lang/Integer;";
invokedynamic   InvokeDynamic
  REF_invokeStatic:
    Example.mybsm:
      "(Ljava/lang/invoke/MethodHandles/Lookup;
        Ljava/lang/String;
        Ljava/lang/invoke/MethodType;)
      Ljava/lang/invoke/CallSite;":
    +:
      "(Ljava/lang/Integer;
        Ljava/lang/Integer;)
      Ljava/lang/Integer;";

Первые четыре инструкции, помещенные целые числа 40 и 2 на стеке и окружает их java.lang.Integer тип обертки. Пятая инструкция вызывает динамический метод. Эта инструкция обращается к постоянной записи пула с a CONSTANT_InvokeDynamic тег:

REF_invokeStatic:
  Example.mybsm:
    "(Ljava/lang/invoke/MethodHandles/Lookup;
      Ljava/lang/String;
      Ljava/lang/invoke/MethodType;)
    Ljava/lang/invoke/CallSite;":
  +:
    "(Ljava/lang/Integer;
      Ljava/lang/Integer;)
    Ljava/lang/Integer;";

Четыре байта следуют CONSTANT_InvokeDynamic тег в этой записи:

В этом примере динамическому сайту вызова дарят упакованные целочисленные значения, которые точно соответствуют тип возможной цели, adder метод. Практически, параметр и типы возврата не должны точно соответствовать. Например, invokedynamic инструкция могла передать или или оба из ее операндов на стеке JVM как примитивный int значения. Или или оба операнда могли также быть невведены Object значения. invokedynamic инструкция могла также получить свой результат как примитив int значение, или невведенный Object значение. В любом случае, dynMethodType параметр mybsm точно опишет тип метода, требуемый invokedynamic инструкция.

Независимо, adder методу, возможно, также дали примитивные или невведенные параметры или возвращаемые значения. Метод начальной загрузки ответственен за составление любого различия между dynMethodType и тип adder метод. Как показано в коде, это легко делается с asType обратитесь к целевому методу.

Ресурсы


Oracle и/или его филиалы Авторское право © 1993, 2011, Oracle и/или его филиалы. Все права защищены.
Свяжитесь с Нами