これは2つの部分からなる質問ですが、個々の部分では意味がありません。バイトコード内の多数のdup
命令は、コードの記述が不十分であることを示していますか?ここで、largeは、すべてのバイトコード命令の一部によって定義されます。dup
さらに、命令を生成するコードをどのように書き直すのですか?
3 に答える
分析している出力について話しているjavac
のですか、それとも独自のコンパイラ/ジェネレーターについて話しているのですか? 何javac
が生成されるかという観点から Java コードの品質に関心がある場合は、忘れてください。まず第一に、javac
次善のバイトコードを生成し、JVM/JIT に依存してすべての最適化を行います (非常に良い選択です)。しかし、それでもバイトコードはおそらく、すぐに思いつくものよりもはるかに優れています。これは、C コンパイラによって生成されたアセンブリ コードの品質について質問するのと似ています。
自分でバイトコードを生成している場合、過剰な数dup
が見栄えが悪いかもしれませんが、パフォーマンスに影響を与えない場合もあります。バイトコードはターゲット マシン上のアセンブリに変換されることに注意してください。JVM はスタック マシンですが、最近のほとんどのアーキテクチャはレジスタ ベースです。使用されるという事実dup
は、一部のバイトコード命令が破壊的であるためです (読み取り時にオペランド スタックから値をポップする)。これはレジスタでは発生しません。必要なだけ何度でも読み取ることができます。例として、次のコードを取り上げます。
new java/lang/Object
dup
invokespecial java/lang/Object <init> ()V
dup
invokespecial
オペランド スタックの一番上にポップするため、ここで使用する必要があります。コンストラクターを呼び出した後にオブジェクトへの参照を失うためだけにオブジェクトを作成するのは、悪い考えのように思えます。しかし、アセンブリにはdup
、データのコピーや複製はありません。を指す 1 つの CPU レジストリだけが作成されjava/lang/Object
ます。
言い換えれば、最適ではないバイトコードは、その場で「より最適な」アセンブリに変換されます。ただ...気にしないでください。
このdup
命令は、オペランド スタックの一番上の要素を単純に複製します。コンパイラは、比較的短いスパン内で値を複数回使用することを認識している場合、値を複製して、必要になるまでオペランド スタックに保持することを選択できます。
最も一般的なケースの 1 つはdup
、オブジェクトを作成して変数に格納する場合です。
Foo foo = new Foo();
を実行javap -c
すると、次のバイトコードが得られます。
0: new #1; //class Foo
3: dup
4: invokespecial #23; //Method "<init>":()V
7: astore_1
英語では、操作はオブジェクトnew
の新しいインスタンスを作成し、コンストラクターを実行します。コンストラクターを呼び出し、変数に格納するためにスタック上の参照が必要なため、使用するのは非常に理にかなっています(特に、変数に格納してから ctor を実行するために取得する代替手段は、Java に違反する可能性があるため)メモリーモデル)。Foo
invokespecial
Foo
dup
これは、Oracle Java Compiler (1.6)が期待どおりに使用されなかったケースです。dup
int x = 12;
public int bar(int z)
{
int y = x + x * 3;
return y + z;
}
式に複数回出現するため、コンパイラはdup
の値を期待します。x
代わりに、オブジェクトから値を繰り返しロードするコードを列挙しました。
0: aload_0
1: getfield #12; //Field x:I
4: aload_0
5: getfield #12; //Field x:I
8: iconst_3
9: imul
10: iadd
dup
2 つのスタック セルが同じキャッシュ ライン上にある可能性が高いのに対し、オブジェクトから値を取得するのは (Hotspot がその魔法を実行した後でも) 比較的コストがかかるため、私はこれを期待していました。
パフォーマンスへの影響とその関係について心配している場合でも、dup
気にしないでください。JVM はジャスト イン タイム コンパイルを行うため、実際にはパフォーマンスに違いはありません。
コードの品質に関しては、Javac がdup
命令を生成する主な原因が 2 つあります。1 つ目は、避けられないオブジェクトのインスタンス化です。2 つ目は、式での即値の特定の使用です。後者が多く見られる場合は、コードの品質が低い可能性があります。通常、ソース コードでそのような複雑な式を使用することは望ましくないためです (可読性が低下します)。
dup
( dup_x1
、dup_x2
、dup2
、dup2_x1
、および)の他のバージョンはdup2_x2
、オブジェクトのインスタンス化でそれらを使用しないため、特に問題があり、ほぼ確実に後者を意味します。もちろん、それでも大きな問題ではありません。つまり、ソース コードが読みにくいということです。
コードが Java からコンパイルされていない場合、すべての賭けは無効になります。特にコンパイラがコンパイル時の最適化を実行する言語では、命令の有無は実際にはあまりわかりません。