14

次のクラスがあると想像してみましょう。

public class Message extends Object {}

public class Logger implements ILogger {
 public void log(Message m) {/*empty*/}
}

および次のプログラム:

public static void main(String args[]) {
  ILogger l = new Logger();
  l.log((Message)null); // a)
  l.log(new Message()); // b)
}

Javaコンパイラはステートメントabを削除しますか?どちらの場合も(ストリッピングするかしないか)、Javaコンパイラの決定の背後にある理由は何ですか?

4

6 に答える 6

17

Javaコンパイラはステートメントaを削除しbますか?

(ソースからバイトコードへのjavac)コンパイラーはどちらの呼び出しも削除しません。(これは、バイトコードを調べることで簡単に確認できます。たとえば、javap -c出力を確認できます。)

どちらの場合も(ストリッピングするかしないか)、Javaコンパイラの決定の背後にある理由は何ですか?

JLSへの準拠:-)。

実用的な観点から:

  • コンパイラーが呼び出しを最適化した場合javac、Javaデバッガーはそれらをまったく見ることができなくなります...これは、開発者にとってかなり混乱を招きます。
  • クラスとメインクラスが独立してコンパイル/変更された場合、 (による)初期の最適化javacは破損につながります。Messageたとえば、次のシーケンスについて考えてみます。

    • Messageコンパイルされ、
    • メインクラスがコンパイルされ、
    • Messagelog何かをするように編集されて...そして再コンパイルされます。

    これで、正しくコンパイルされていないメインクラスができました。これは、時期尚早にインライン化されたコードが古くなっているためですab


ただし、JITコンパイラは、さまざまな方法で実行時にコードを最適化する場合があります。例えば:

  • メソッドが呼び出されabJITコンパイラが仮想メソッドのディスパッチが不要であると推測できる場合は、インライン化される可能性があります。(これLoggerを実装するアプリケーションで使用されるクラスがIfだけである場合ILogger、優れたJITコンパイラーにとっては簡単です。)

  • 最初のメソッド呼び出しをインライン化した後、JITコンパイラーは、本体がnoopであると判断し、呼び出しを最適化する場合があります。

  • Message2番目のメソッド呼び出しの場合、JITコンパイラーは、オブジェクトをヒープに割り当てる必要がないことを(エスケープ分析によって)さらに推測できます...または実際にはまったくありません。

((プラットフォーム上の)JITコンパイラーが実際に何をするかを知りたい場合、ホットスポットJVMには、選択したメソッドのJITコンパイル済みネイティブコードをダンプするJVMオプションがあります。)

于 2013-01-08T03:37:26.320 に答える
6

次のファイルを(を使用してjavap -c)逆アセンブルすると、バイトコードにコンパイルするときに1.7.0コンパイラによってファイルが削除されないことがわかります。

public class Program
{
    public static class Message extends Object {}

    public interface ILogger {
        void log(Message m);
    }

    public static class Logger implements ILogger {
        public void log(Message m) { /* empty */ }
    }

    public static void main(String[] args) {
        ILogger l = new Logger();
        l.log((Message)null); // a)
        l.log(new Message()); // b)
    }
}

結果は以下のとおりです。重要なビットは、13行目と26行目の呼び出しです。

Compiled from "Program.java"
public class Program {
  public Program();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class Program$Logger
       3: dup
       4: invokespecial #3                  // Method Program$Logger."<init>":()V
       7: astore_1
       8: aload_1
       9: aconst_null
      10: checkcast     #4                  // class Program$Message
      13: invokeinterface #5,  2            // InterfaceMethod Program$ILogger.log:(LProgram$Message;)V
      18: aload_1
      19: new           #4                  // class Program$Message
      22: dup
      23: invokespecial #6                  // Method Program$Message."<init>":()V
      26: invokeinterface #5,  2            // InterfaceMethod Program$ILogger.log:(LProgram$Message;)V
      31: return
}

編集:ただし、@ mikeraが指摘したように、プログラムの実行時にJITコンパイラーがさらに最適化を行う可能性があります。これにより、呼び出しを排除できる可能性があります。残念ながら、詳細についてはコメントできません。

補足:ホットスポットJVMで使用されるパフォーマンス手法を扱ったこのリンクに興味があるかもしれません。

https://wikis.oracle.com/display/HotSpotInternals/PerformanceTechniques

于 2013-01-08T02:50:21.487 に答える
3

おそらく最終的には、間違いなくすぐにではなく、必ずしもそうとは限りません。JITは、特に2、3回しか呼び出されないメソッドについては保証しません。(おそらく、呼び出しを単にインライン化logするものとして分類され、インライン化されたコードはたまたま...何もありません。)

于 2013-01-08T02:44:48.607 に答える
3

明確に言うことは不可能です-それはJVM/Javaコンパイラの実装に依存します。

十分に賢いコンパイラは、どちらのステートメントも効果がないことを証明できるため、それらを排除できます。最新のJVMのほとんどがこれを実行すると思いますが、特定の構成でテストして確認する必要があります。

a)はb)よりも最適化が容易です。b)にはコンストラクター呼び出しが含まれているため、コンパイラーはステートメント全体を最適化する前に副作用がないことを証明する必要があります。

この種の除去は、Javaコンパイラ自体ではなくJITコンパイラによって行われると予想されることに注意してください。つまり、ログ関数呼び出しを含むバイトコードが生成される可能性がありますが、これは後でJITコンパイラがコンパイルするときに最適化されます。ネイティブコードまで。

また、JITは実行時統計などで再コンパイルできるため、コードは最初からそこにある可能性がありますが、後で最適化を行うとコンパイルされなくなります。

于 2013-01-08T02:48:25.923 に答える
1

呼び出されたメソッドが空であるため、Javaコンパイラが呼び出しを削除することはないと思います。これは、メインメソッドを変更せずに、将来的にメソッドを変更できるためです。

于 2013-01-08T02:45:55.170 に答える
1

サーバーJITは、参照をfinalにすると、確実にインライン化され、最終的にコードを完全に削除します。final ILogger l = new Logger(); 最新のJVMでは、ほとんどの最適化はJITによって実行されます。

于 2013-12-06T22:44:37.863 に答える