4

まず第一に、私は「時期尚早の最適化」恐怖症の人たちに私を惜しまないように頼みます:私は何も最適化したくありません、私はただ興味があります。

私はstackoverflowを含む2つのことを読んだ/観察しました(現在リンクを見つけることができません):

  • メソッド呼び出しの場合、すべてのローカル変数のメモリは、メソッドの「最初」に予約されます。つまり、下位レベルのスコープで宣言された変数のメモリも予約されます(これは科学的に不適切な表現であることがわかっています。たとえば、呼び出しメカニズムの動作を無視するなどです。 、しかし、ポイントが明確であることを願っています)。明らかに、実行中のプログラムのスコープは存在しません。スコープは、読みやすさ、保守性、コードの構造化、コンパイラーへの意図(最適化のヒントの提供など、以下を参照)を向上させるために、ソースコードレベルでのみ存在します。
  • 可能な限り最小のスコープ(つまり、まだ必要な最高レベル)で変数を宣言することの利点の1つは、「コンパイラーが(他のブロックの)他の一時変数にメモリーを再利用できる」ことです。これは私には明確で論理的に聞こえます。

コンパイラ/JIT/何が実際に最適化できるのか、そして何ができないのか疑問に思っています。

次の方法があります(変数が実際に使用されていると仮定します。そのため、変数を最適化することはできません)。

// The method does many (useful) things, but these were cut here
public void myMethod() {
    int var1 = 1;
    ... // do work
    if (something) {
        int var2 = 2;
        int var3 = 3;
        ... // do work
    }
    int var4 = 4;
    int var5 = 5;
    ... // do work
}

1.)コンパイラーは、およびのスペースを検出して、var2およびのスペースをvar3再利用できますか?分解されたバイトコードでそのようなことを見たのを覚えていません。var4var5

2.)上記のコードメソッドは、メソッドの最後が{}に設定されている場合と同等ですか?

public void myMethod() {
    int var1 = 1;
    ... // do work
    if (something) {
        int var2 = 2;
        int var3 = 3;
        ... // do work
    }
    {
        int var4 = 4;
        int var5 = 5;
        ... // do work
    }
}

最後に、より単純なケースを見てみましょう。

public void myMethod() {
    int var1 = 1;
    ... // do work, and then don't refer to var1 any more

    int var4 = 4;
    int var5 = 5;
    ... // do work      
}

3.)この場合、メモリは(または)var1に再利用できますか?つまり、このような場合、メソッドには3つではなく2つのローカル変数用のメモリがあれば十分です。var4var5int

(理論的には、これらはコンパイラーにとって明らかなケースであることを私は知っていますが、コンパイラーが何もできない、または想定できない理由など、物事を見落とすことがあります。)

4

2 に答える 2

3

私は実際のCPUのコンパイラについて話すことができますが、JVMコンパイラについてはよくわかりません。また、コンパイルレベルでは、コードが思ったほど最適化されているとは思いません(Javaプラットフォームは、メモリのフットプリントについてあまり気にしません。想像できるでしょう)。

実際のコンパイラの場合、これらのシナリオは実際には非常に頻繁に最適化されます。これは、高水準言語レベルではなく、RTLレベルのような低中級レベルで行われます。すべてがレジスタ割り当てまたはスタック割り当ての目的で作成され、関数内の変数のリビングセットを計算することによって機能します。

これは、コードがコンパイルされると、任意の数の一時レジスタを想定してすべてがRTLに変換され、一時レジスタごとに、いわゆるライブ変数分析によってライブ状態が計算されることを意味します。これは、そのようなことを最適化する方法の1つにすぎません。

例えば

  • var1ライブスコープは命令1から10までです
  • var2ライブスコープは命令5から17までです
  • var3ライブスコープは命令3から25までです
  • 等々

これは、コードをジャンプやラベルを含まないブロックに分割した後に実行されるため、指定したブロック内のフローを確認できます。

この計算の後、ほとんどの時間に必要な変数を簡単に確認できます。これらの変数は役に立たなくなり、予約済みのスペースを解放できます。これは、可能な限り多くの変数をレジスターに適合させ、アセンブルされたコードを大幅に最適化できるようにするために行われます。

個人的には、 javacがこれを行うとは思いません(JVMがスタックベースであるため、最近は必要なくオブジェクトのメモリ割り当てが壊れてしまうためです)が、私の推測です。

于 2013-01-05T04:27:32.140 に答える
1
  1. はい。スタックスロットはネストされた}の後で再利用できますが、javacが再利用するのを見てきました。

  2. はい。ケース1と2は同等です。2番目のネストされた{}は、何かがそれに続く場合を除いて、何も変更しません。

  3. Javaコンパイラはこれを最適化しませんが、理論的には、考えられるすべての適切な条件が与えられます。変数がfinalの場合、それらにスロットをまったく割り当てない可能性があります。

もう1つ注意すべき点は、ネストされた}に対応するバイトコード命令がないため、JVM、したがってHotSpotには、ネストされたスコープがどこで終了し、スタックスロットが使用法を変更するかを知る根拠がないことです。

于 2013-01-05T04:34:01.430 に答える