私はJITコンパイラがやっている正確に何を話すことはできませんが、あなたは(完全にループ本体をスキップしても安全であることを決定するために)行うには、それを求めているの最適化が実際に行うことは極めて困難です私はそれが完了したことを非常に疑う。これは、文字列が「基本的なJavaクラス」であるかどうかに関係なく当てはまります。
文字列の代わりに、任意のクラスFooのインスタンスを作成しているとします。私たちが2つのことを知っていれば、それらのFooオブジェクトの作成をすべてスキップするだけで安全です:new Foo()
という呼び出しは、観察可能な副作用を持たなかった。 Fooへの参照がループ本体を「エスケープ」していないことを示しています。
目に見える副作用は、静的メンバーの値を設定するようなものです(例えば、Fooクラスが常にFoo()
の静的カウントを保持していた場合など)。参照のエスケープの例はFoo()
の内部のthis
変数が別の場所に渡された場合です。
Foo()
を見るだけでは不十分であることに注意してください。Fooのスーパークラスのコンストラクタ(そしてオブジェクトのチェーンまで)を見る必要があります。そして、それらのオブジェクトのそれぞれの初期化時に実行されるすべてのコードを調べる必要があります。そして、そのコードによって呼び出されるすべてのコードを見てください。それは、「ジャストインタイム」を行うための膨大な分析になります。
public class Foo extends Bazz{
static int count = 0;
public Foo(){
// Implicit call to Bazz() has side effect
count++; // side effect
Bazz.onNewFoo(this); // reference escaping
}
Bazz bazz = new Bazz(); // side effect
{
Bazz.onNewBazz(this.bazz); // reference escaping
}
}
class Bazz{
static int count = 0;
static List<Foo> fooList = new LinkedList<>();
static List<Bazz> bazzList = new LinkedList<>();
static void onNewFoo(Foo foo){
fooList.add(foo);
}
static void onNewBazz(Bazz bazz){
bazzList.add(bazz);
}
public Bazz(){
count++;
}
}
javacにこの分析と最適化をさせてください。その問題は、コンパイル時にクラスパスにあったFoo()
のバージョンが、実行時にクラスパスにあるものと同じになることを保証する方法がないことです。 (これはJavaの非常に貴重な機能です - アプリケーションをGlassFishからTomcatに移動させ、再コンパイルせずに済ませることができます)。したがって、コンパイル時に行われた分析は信頼できません。
最後に、StringがFooと変わらないことを認識してください。私たちはまだ解析を実行する必要があり、事前にその解析を行う方法はありません(アプリケーションを再コンパイルせずに私のJREをアップグレードすることができます)。
バイトコードレベルでは基本的に最適化は行われません。ほぼすべてが実行時です。 JITは、あなたが保証することができない究極のマジックブラックボックスですが、ループ全体が消滅する可能性はありません。 –
JVMに次のオプションを使用して、JITがコードをどのように処理するかを見ることができます: '-XX:+ PrintCompilation -XX:+ UnlockDiagnosticVMOptions -XX:+ PrintInlining'。 – fge
@fgeその出力を解読することは非常に困難です。私はそこで何が起こっているか完全にはわからない。 – bluejamesbond