次の項目について説明します。
Java SEプラットフォームは、次の特徴を持つアプリケーションの開発を可能にします。
Java SEプラットフォームは次の領域およびその他の領域に対する堅牢なサポートも提供します。
OracleのHotSpot JVMは次のツールおよび機能も提供します。
Java以外の言語は、Java SE 7プラットフォームを介してJVMのインフラストラクチャを利用することで、パフォーマンスを潜在的に最適化できます。主要なメカニズムはinvokedynamic
命令で、動的型付け言語のコンパイラおよびランタイム・システムのJVMへの実装を簡略化します。
プログラミング言語は、コンパイル時に型チェックを実行する場合、静的に型付けされます。型チェックとは、プログラムが型安全であることを確認する処理のことです。プログラムのすべてのオペレーションの引数が正しい型であれば、そのプログラムは型安全です。
Javaは静的型付け言語です。クラス変数とインスタンス変数、メソッド・パラメータ、戻り値、およびその他の変数のすべての型付け情報は、プログラムがコンパイルされるときに利用されます。Javaプログラミング言語のコンパイラはこの型情報を使用して強く型付けされたバイト・コードを生成することで、JVMでの実行時に効率的な実行が可能になります。
次のHello Worldプログラムの例は、静的型付けを示しています。型は太字で示しています。
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プログラミング言語で記述されたHello Worldプログラムの例です。
#!/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はFixnum
型を持つ数字2を文字列に暗黙的にキャストしません。
2つの数字(どの数値型でも構いません)を追加してその合計を返す、次の動的型付けメソッドaddtwo
について考えます。
def addtwo(a, b) a + b; end
あなたの組織が、メソッドaddtwo
が記述されたプログラミング言語用のコンパイラとランタイム・システムを実装しているとします。静的型付けか動的型付けかにかかわらず、強く型付けされた言語では、+
(加算演算子)の動作はオペランドの型によって決まります。静的型付け言語のコンパイラは、a
およびb
の静的型に基づいて、+
のどの実装が適切かを選択します。たとえば、a
およびb
の型がint
の場合、JavaコンパイラはJVMのiadd
命令で+
を実装します。JVMのiadd
命令は静的に認識されるオペランド型を必要とするため、この加算演算子はメソッド呼出しにコンパイルされます。
これに対し、動的型付け言語のコンパイラは実行時まで選択を保留する必要があります。文a+b
はメソッド呼び出し+(a, b)
としてコンパイルされます(+
はメソッド名)。(JVMでは+
という名前のメソッドが許可されますが、Javaプログラミング言語では許可されません。)この動的型付け言語用のランタイム・システムが、a
およびb
が整数型の変数であることを識別できるとします。ランタイム・システムは、任意のオブジェクト型ではなく整数型に専用化された+
の実装を呼び出すことを選択します。
動的型付け言語のコンパイルの課題は、プログラムがコンパイルされた後に、メソッドまたは関数の最適な実装を選択できるランタイム・システムをどのように実装するかです。すべての変数をObject
型のオブジェクトとして扱うと効率的に機能しません。Object
クラスに+
という名前のメソッドが含まれていないためです。
Java SE 7は、ランタイム・システムがコール・サイトとメソッド実装との間のリンケージをカスタマイズできる、invokedynamic
命令を導入しています。この例では、invokedynamic
コール・サイトは+
です。invokedynamic
コール・サイトは、ブートストラップ・メソッド (サイトをリンクするためにJVMによって1回呼び出される、動的型付け言語のコンパイラによって指定されるメソッド)によって、メソッドにリンクされます。+
を呼び出すinvokedynamic
命令をコンパイラが発行したと想定し、かつランタイム・システムがメソッドadder(Integer,Integer)
を認識すると想定したうえで、ランタイムは次のようにinvokedynamic
コール・サイトをadder
メソッドにリンクできます。
class IntegerOps { public static Integer adder(Integer x, Integer y) { return x + y; } }
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
のstaticメソッド・ハンドルを作成します。
注: このブートストラップ・メソッドはadder
メソッドで定義されたコードにのみinvokedynamic
コール・サイトをリンクし、invokedynamic
コール・サイトに指定される引数はInteger
オブジェクトであると想定します。ブートストラップ・メソッドは、ブートストラップ・メソッドのパラメータ(この例ではcallerClass
、dynMethodName
、およびdynMethodType
)が変化すると実行されるコードにinvokedynamic
コール・サイトを正しくリンクするために、追加コードを必要とします。
クラスjava.lang.invoke.MethodHandles
およびjava.lang.invoke.MethodHandle
には、既存のメソッド・ハンドルに基づいてメソッド・ハンドルを作成するさまざまなメソッドが含まれています。この例では、メソッド・ハンドルmh
のメソッド・タイプがパラメータdynMethodType
で指定されたメソッド・タイプと一致しない場合に、メソッドasType
を呼び出します。これにより、ブートストラップ・メソッドはinvokedynamic
コール・サイトをメソッド・タイプが厳密に一致しないJavaメソッドにリンクできます。
ブートストラップ・メソッドによって返されるConstantCallSite
インスタンスは、個々のinvokedynamic
命令に関連付けられているコール・サイトを表します。ConstantCallSite
インスタンスのターゲットは永続的で変更できません。この場合、Javaメソッドはadder
1つのみ存在します(コール・サイト実行の候補)。このメソッドはJavaメソッドでなくてもかまいません。代わりに、ランタイム・システムで使用できるこのようなメソッドがいくつかあり、それぞれが異なる引数型を処理する場合、ブートストラップ・メソッドmybsm
は、dynMethodType
引数に基づいて正しいメソッドを動的に選択できます。
invokedynamic
命令は、動的言語のコンパイラおよびランタイム・システムのJVM上への実装を簡略化し、潜在的に改善します。invokedynamic
命令は、言語実装者がカスタム・リンケージ動作を定義することを許可することで、これを行います。この点は、invokevirtual
などその他のJVM命令とは異なります(Javaクラスおよびインタフェースに固有のリンケージ動作がJVMによって固定されている)。
invokedynamic
命令の各インスタンスは動的コール・サイトと呼ばれます。動的コール・サイトは、最初はリンクされていない状態(呼び出すコール・サイトにメソッドが指定されていない)です。前述のように、動的コール・サイトはブートストラップ・メソッドによってメソッドにリンクされます。動的コール・サイトのブートストラップ・メソッドは、動的型付け言語のコンパイラによって指定されるメソッドで、サイトをリンクするためにJVMによって1回呼び出されます。ブートストラップ・メソッドから返されるオブジェクトは、コール・サイトの動作を永続的に決定します。
invokedynamic
命令には、定数プール・インデックス(その他のinvoke
命令と同じ形式)が含まれます。定数プール・インデックスはCONSTANT_InvokeDynamic
エントリを参照します。このエントリは、ブートストラップ・メソッド(CONSTANT_MethodHandle
エントリ)、動的にリンクされるメソッドの名前、および動的にリンクされるメソッドへの呼出しの引数の型および戻り型を指定します。
次は、invokedynamic
命令の例です。この例では、ランタイム・システムがブートストラップ・メソッドExample.mybsm
を使用することで、このinvokedynamic
命令(+
、加算演算子)で指定された動的コール・サイトをIntegerOps.adder
メソッドにリンクします。メソッド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;";
注: これらのセクションのバイト・コード例は、ASM Javaバイト・コード操作および分析フレームワークの構文を使用しています。
動的にリンクされるメソッドをinvokedynamic
命令で呼び出すには、次の手順が必要です。
実行時に、JVMがはじめてinvokedynamic
を検出すると、ブートストラップ・メソッドが呼び出されます。このメソッドは、invokedynamic
命令によって指定された名前を、実行されるべきコード(ターゲット・メソッド、メソッド・ハンドルによって参照される)にリンクします。JVMが同じinvokedynamic
命令を再度実行した場合、ブートストラップ・メソッドを呼び出さず、リンクされたメソッド・ハンドルを自動的に呼び出します。
ブートストラップ・メソッドの戻り型はjava.lang.invoke.CallSite
である必要があります。CallSite
オブジェクトは、invokedynamic
命令とそれがリンクされるメソッド・ハンドルの、リンク状態を表します。
ブートストラップ・メソッドは3つ以上のパラメータを取ります。
MethodHandles.Lookup
オブジェクト、invokedynamic
命令のコンテキスト内でメソッド・ハンドルを作成するファクトリ。String
オブジェクト、動的コール・サイト内で言及されるメソッド名。MethodType
オブジェクト、動的コール・サイトの解決済み型シグニチャ。invokedynamic
命令への1つ以上の追加静的引数。定数プールから取り出されるこれらの引数の目的は、言語実装者がブートストラップ・メソッドに便利な追加メタデータを安全かつコンパクトにエンコードするのを支援することです。各コール・サイトには独自のブートストラップ・メソッドが指定される可能性があるため、原則として名前と追加引数は冗長になります。ただし、そのような運用ではおそらく、大きなクラス・ファイルや定数プールが生成されます。ブートストラップ・メソッドの例については、「動的型付け言語のコンパイルの課題」セクションを参照してください。
前述のように、invokedynamic
命令にはタグCONSTANT_InvokeDynamic
を持つ定数プール内のエントリへの参照が含まれます。このエントリには、定数プール内のその他のエントリへの参照および属性への参照が含まれます。このセクションでは、invokedynamic
命令で使用される定数プール・エントリについて簡単に説明します。詳細は、java.lang.invokeパッケージのドキュメントおよび「Java仮想マシン仕様」を参照してください。
次は、メソッド+
をJavaメソッドadder
にリンクするブートストラップ・メソッドExample.mybsm
を含む、クラスExample
の定数プールからの引用です。
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
命令の定数プール・エントリには、3つの値が含まれています。
CONSTANT_InvokeDynamic
タグ0
#234
。値0
は、BootstrapMethods
属性に格納されている指定子配列内の最初のブートストラップ・メソッド指定子を参照します。ブートストラップ・メソッド指定子は、定数プール表内にはありません。この独立した指定子配列に含まれています。各ブートストラップ・メソッド指定子には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
のメソッド・ハンドルの定数プール・エントリには3つの値が含まれています。
CONSTANT_MethodHandle
タグ6
#232
。値6
はサブタグREF_invokeStatic
です。このサブタグの詳細については、次のセクション「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;";
最初の4つの命令は、整数40
と2
をスタック上に置き、それらをjava.lang.Integer
ラッパー型にAutoboxingします。5つ目の命令が動的メソッドを呼び出します。この命令は、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
タグに4つのバイトが続きます。
最初の2つのバイトが、ブートストラップ・メソッド指定子を参照するCONSTANT_MethodHandle
エントリへの参照を構成しています。
REF_invokeStatic: Example.mybsm: "(Ljava/lang/invoke/MethodHandles/Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType;) Ljava/lang/invoke/CallSite;"
前述のように、ブートストラップ・メソッド指定子へのこの参照は、定数プール表内にはありません。BootstrapMethods
と呼ばれるクラス・ファイル属性によって定義された、独立した配列に含まれます。ブートストラップ・メソッド指定子にはCONSTANT_MethodHandle
定数プール・エントリ(ブートストラップ・メソッドそのもの)へのインデックスが含まれています。
このCONSTANT_MethodHandle
定数プール・エントリには3つのバイトが続きます。
最初のバイトはサブタグREF_invokeStatic
です。これは、このブートストラップ・メソッドがstaticメソッドのメソッド・ハンドルを作成することを意味します。このブートストラップ・メソッドが動的コール・サイトをstatic Javaメソッドadder
にリンクしていることに注意してください。
次の2つのバイトは、メソッド・ハンドル作成対象のメソッドを表す、CONSTANT_Methodref
エントリを構成しています。
Example.mybsm: "(Ljava/lang/invoke/MethodHandles/Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType;) Ljava/lang/invoke/CallSite;"
この例では、ブートストラップ・メソッドの完全指定名がExample.mybsm
、引数型がMethodHandles.Lookup
、String
、およびMethodType
、戻り型がCallSite
です。
次の2つのバイトがCONSTANT_NameAndType
エントリへの参照を構成しています。
+: "(Ljava/lang/Integer; Ljava/lang/Integer;) Ljava/lang/Integer;"
この定数プール・エントリは、メソッド名(+
)、引数型(2つのInteger
インスタンス)、および動的コール・サイトの戻り型(Integer
)を指定しています。
この例では、動的コール・サイトにはAutoboxingされた整数値(最終ターゲットであるadder
メソッドの型に厳密に一致)が渡されます。実際には、引数型と戻り型が厳密に一致する必要はありません。たとえば、invokedynamic
命令はJVMスタックにそのオペランドの一方または両方をプリミティブ型int
値として渡すことができます。オペランドの一方または両方が型指定のないObject
値でもかまいません。invokedynamic
命令は、その結果をプリミティブ型int
値または型指定のないObject
値として受け取ることもできます。どの場合も、mybsm
のdynMethodType
引数がinvokedynamic
命令によって要求されるメソッド型を正確に記述します。
adder
メソッドに渡されるのは、プリミティブ型でも、型指定のない引数でも、戻り値でもかまいません。ブートストラップ・メソッドが、dynMethodType
とadder
メソッドの型の違いを解決します。コードに示すように、これはターゲット・メソッドでasType
を呼び出すことで簡単に実行されます。