State of Valhalla – Section 3: JVM Model

原文はこちら。
The original article was written by Brian Goetz (Java Architect, Oracle).
http://cr.openjdk.java.net/~briangoetz/valhalla/sov/03-vm-model.html

この文書はインラインクラス(inline class)のJava Virtual Machineの見解を説明しており、Java言語の見解とは必ずしも一致しないことにご注意ください。読者がこの文書からJava言語に関する結論を導く際には注意が必要です。

Section 1: The Road to Valhalla英語日本語
Section 2: Language Model英語日本語
Section 3: JVM Model英語日本語
Section 4: Translation英語日本語

Inline classes

Valhalla以前は、全てのオブジェクト、具体的にはクラスや配列のインスタンスには一意のオブジェクトIDがありました。Valhallaを使うとクラスがACC_INLINEフラグでクラスをマークすることで、インスタンスにIDを持たせるか(アイデンティティクラス、identity class)、持たせないか(インラインクラス)を選択できます。

(クラスファイルで記述される)抽象仮想マシンでは、オブジェクト参照を使って排他的にインラインオブジェクト、アイデンティティオブジェクトの両方を参照します。しかし、JVMはインラインオブジェクトがアイデンティティを持たないことを知っているので、レイアウト(平坦化)、インスタンス化、メンバーアクセス、呼び出し規約(スカラー化)を日常的に最適化できます。

Carriers and basic types

JVMの型ディスクリプタ(JVMS 4.3.2)は、接頭辞を使って基本型を表現します。

The Java® Virtual Machine Specification – Java SE 14 Edition
4.3.2. Field Descriptors
https://docs.oracle.com/javase/specs/jvms/se14/html/jvms-4.html#jvms-4.3.2

現在、8個のプリミティブ型に対応する8個の基本型(I、Jなど)と、オブジェクト参照に対応する1個の基本型(L)があります(文字Vは型ディスクリプタには存在しますが、型とは見なされていません)。

これらの9個の基本型は5個のキャリア型(int、long、float、double、オブジェクト参照)に対応しており、これはスタックスロットやローカル変数スロットでの値の異なる表現に対応しています(これらの2組の違いはbyte、short、char、booleanをintにしてしまうこと、そしてIキャリアを使っていることに由来します)。プリミティブキャリアは値を直接スタックやローカル変数スロットに格納します(floatとdoubleの値は隣接する2個のスロットに格納)。オブジェクト参照(L)キャリアはオブジェクト参照を対応するスロットに格納します。

インライン型を表現するために、インラインオブジェクトへの参照を表現する新たな基本型Qを追加します。QディスクリプタはLディスクリプタ同様の構文構造を有します(例:Qjava/lang/int;)。

Lディスクリプタの構文形式を再利用するのに加えて、QディスクリプタはLキャリアも再利用します。JVMでは、Qディスクリプタの下では参照がnullにはなり得ないという事実を除いて、インラインオブジェクトへの参照とアイデンティティオブジェクトへの参照で構造に違いはありません。

LディスクリプタかQディスクリプタかの選択は名前付きクラスがアイデンティティクラスもしくはインラインクラスを解決するかと強く関連しています。Lディスクリプタがインラインクラスを解決しようとするとリンケージエラーですし、Qディスクリプタがアイデンティティクラスを解決しようとする場合も同様にリンケージエラーです(個別の基本型デジグネータ(指示子、designator)の必要性は、フィールドレイアウト時など、アイデンティティクラスの場合に比べてより積極的にインラインクラスをプリロードしなければならないという事実に起因しています)。

Supertypes

インラインクラスはインターフェースを実装でき、制限された抽象クラスならびに特別なクラスであるObjectを拡張できます(抽象クラスへの制約にはフィールドを持たないこと、空のコンストラクタが含まれます)。 これはつまり、インターフェース型の変数、適切な抽象クラス型もしくはObjectがアイデンティティオブジェクトもしくはインラインオブジェクトへの参照の可能性がある、ということです。JVMはこのような拡張を通常のサブタイプとして取り扱うため、インラインオブジェクトへの参照をObjectもしくは適切なインターフェース、抽象クラス型への拡張をキャストせずに実現できます。インラインクラスはコンストラクタを持ちませんが、そのかわりに静的ファクトリメソッド(名前は<new>)を持ちます。

Restrictions

インラインクラスにはアイデンティティクラスに比べて制約があります。インラインクラスのインスタンスフィールドにはACC_FINALがマークされていなければならず、InlineObjectというインターフェースの実装が必要です(直接的、間接的を問わず、IdentityObjectの実装は許されていません)。インラインクラスがObject以外のクラスを拡張する場合、そのクラスは以下の条件を満たす必要があります。なお、インラインクラスの静的ファクトリは、定義上空でなければならないため、スーパークラスのコンストラクタを呼び出しません。

  • abstractである
  • フィールドを持たない
  • 空の引数を取らないコンストラクタを持つ(ACC_ABSTRACTとマークされる可能性がある)

インラインクラスVのフィールドは直接的にせよ間接的にせよ、型Vのフィールドを持つことは許可されません。

Bytecodes

インラインクラスの組み込みにより、数多くのバイトコードに影響があります。あるものは新しい、別のものは追加された振る舞いや制約が入っています。

既存の a* バイトコードが拡張され、アイデンティティクラス、インラインクラスの両方への参照を一様にサポートするようになりました。

インラインクラスのコンストラクションにために新たに2個のバイトコードを使います。

  • defaultvalue は、インラインクラス・インスタンスのための new の類似体(alanogue)です。インラインクラスのデフォルトインスタンス(全てゼロ)への参照をスタックに残します(初期化されていないフィールドや配列要素も、defaultvalueによってあたかも初期化されているかのように、その型のデフォルト値に初期化されます)。
  • withfieldは、インラインクラス・インスタンスのためのputfieldの類似体(alanogue)です。これはスタック上のインラインオブジェクトと新たなフィールド値を取り、指定されたフィールド以外は元のインスタンスと全く同じフィールドを持つインラインクラスのインスタンスへの参照を残します。

withfieldバイトコードは制限されており、変更されるインラインクラスを宣言するクラスしか実行できません。これは新たな値の作成をカプセル化するため、実装を避ける任意の値がその不変条件を順守していると見られることをクラスが保証できます。newバイトコードとputfieldバイトコードはインラインクラスのインスタンスでは利用できません。withfieldバイトコードはアイデンティティクラスでは利用できません。

aastore命令は、インラインクラスインスタンスの配列に値を格納する前に、要素値に対するnulチェックを実行します。

The acmp* acmp* 命令はオペランド間のより高度な比較を実行します。2個のオブジェクト参照が等しい条件は以下の通りです。

  • 両者がnullの場合
  • 両者が同じアイデンティティオブジェクトへの参照の場合
  • 両者が同じ型のインラインオブジェクトへの参照で、全てのフィールドが等価である場合(等価とは適切な等値比較をフィールドに再帰的に適用すること。等値比較にはプリミティブに対する通常の等値(Float::equalsとDouble::equalsのセマンティクスで比較されるFloatとDoubleフィールドを除く)を、参照にはacmpを使用)

if_acmpnull命令をインラインオブジェクトへの参照に適用した場合、常にfalseを返します。

From Q-World to L-World

Valhallaプロトタイプの以前のイテレーションで、QディスクリプタはLキャリアと互換性がない個別のキャリアを持っていました(明示的な変換がなければプリミティブが参照型との互換性がないのと同じです)。そしてプリミティブのように、Q型にはスーパータイプ(supertype)がありませんでした。インターフェースとObjectメンバーへのアクセスはL型のコンパニオンクラスに変換して実行する必要がありました(boxとして機能したものの、今日のboxの偶然の一致はありませんでした)。

同様に、インラインクラスには以前は独自のデータ移動のバイトコード(v*)がありましたが、L-Worldでは、a*バイトコードを使ってアイデンティティオブジェクトへの参照、インラインオブジェクトへの参照の両方を一様に移動します。最後に、Q-Worldではインラインオブジェクトの配列はObjectの配列の共変(covariant)ではありません。

この区別により、インラインクラスを「強化されたプリミティブ」(enhanced primitives)として扱いました。そのため、良い意味でも悪い意味でも、クラスのようにコーディングできたものの、まるでintのように振る舞いました。インラインクラスとアイデンティティクラス間に全てのレベル(ディスクリプタ、バイトコード、サブタイプの関係)で継ぎ目があったため、既存のアイデンティティクラスからインラインクラスへの移行が非常に難しくなりました。上記アプローチが生み出した課題によって、L-Worldの設計は顕著に特徴付けられました。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中