私は最近、 Java Virtual Machine Specifications (JVMS)を見て、プログラムが機能する理由をよりよく理解しようと試みましたが、まだ手に入らないセクションが見つかりました。 。
セクション 4.7.4 はStackMapTable属性を説明し、そのセクションではスタックマップフレームに関する詳細を説明します。問題は、それが少し冗長だということであり、私は例によって最もよく学びます。読むことではありません。
私は最初のスタックマップフレームがメソッド記述子から派生していることを理解していますが、どのように(おそらく ここ で説明されているのか)わかりません行う。 Javaのブロックに似ていると思いますが、お互いにスタックマップフレームを持つことはできないようです。
とにかく、2つの特定の質問があります。
そして1つの一般的な質問:
Javaは、サンドボックスのセキュリティを維持し、コードを安全に最適化するために、ロードされるすべてのクラスを検証する必要があります。これはバイトコードレベルで行われるため、検証ではJava言語の不変式をnot検証します、バイトコードのルールに従ってバイトコードが意味をなすことを確認するだけです。
特に、バイトコードの検証により、命令の形式が整っていること、すべてのジャンプがメソッド内の有効な命令であること、およびすべての命令が正しい型の値で動作することを確認します。最後の1つは、スタックマップの出番です。
問題は、バイトコード自体に明示的な型情報が含まれていないことです。タイプは、データフロー分析を通じて暗黙的に決定されます。たとえば、iconst命令は整数値を作成します。スロット1に保存すると、そのスロットにはintが追加されます。代わりにフロートを格納するコードから制御フローがマージされる場合、スロットは無効なタイプであると見なされるようになりました。つまり、上書きするまでその値でこれ以上何もできないということです。
歴史的に、バイトコード検証はこれらのデータフロールールを使用してすべてのタイプを推測していました。残念ながら、後方へのジャンプはすでに推論された型を無効にする可能性があるため、バイトコードを通る単一の線形パスですべての型を推論することは不可能です。従来のベリファイアは、すべての変更が停止するまでコードを繰り返し処理することでこれを解決し、潜在的に複数のパスを必要としました。
ただし、Javaでは検証によりクラスのロードが遅くなります。オラクルは、単一のパスでバイトコードを検証できる新しい高速検証器を追加することで、この問題を解決することにしました。これを行うには、Java 7(移行中にJava 6で始まるすべての新しいクラスが必要でした)状態)バイトコードが単一のパスで検証できるように、それらのタイプに関するメタデータを保持します。バイトコード形式自体は変更できないため、この型情報はStackMapTable
という属性に個別に保存されます。
コード内のすべての単一ポイントにすべての単一値の型を格納するだけでは、明らかに多くのスペースを占有し、非常に無駄になります。メタデータをより小さく、より効率的にするために、彼らはジャンプのターゲットである位置のタイプのみをリストすることを決めました。あなたがそれについて考えるなら、これはあなたがシングルパス検証を行うために追加の情報を必要とする唯一の時間です。ジャンプターゲット間では、すべての制御フローは線形であるため、古い推論ルールを使用して、位置間の型を推測できます。
型が明示的にリストされている各位置は、スタックマップフレームと呼ばれます。 StackMapTable
属性にはフレームのリストが順番に含まれていますが、通常、データサイズを削減するために前のフレームとの違いとして表されます。メソッドにフレームが存在しない場合、つまり制御フローが結合しない場合(つまり、CFGがツリーである場合)、StackMapTable属性を完全に省略することができます。
これが、StackMapTableの仕組みと追加された理由の基本的な考え方です。最後の質問は、暗黙の初期フレームがどのように作成されるかです。もちろん、答えは、メソッドの最初ではオペランドスタックが空であり、ローカル変数スロットはメソッドパラメーターの型で指定された型を持ち、メソッドパラメーターはメソッドデクリプターから決定されるということです。
Javaに慣れている場合、メソッドパラメータタイプがバイトコードレベルでどのように機能するかにわずかな違いがあります。まず、仮想メソッドには、最初のパラメーターとして暗黙的なthis
があります。次に、boolean
、byte
、char
、およびshort
がバイトコードレベルに存在しません。代わりに、それらはすべて舞台裏でintとして実装されます。