12

同一のJavaソースがバイナリの異なるクラスファイルにコンパイルされる方法を誰か説明できますか?

この問題は、次の状況から生じます。

かなり大きなアプリケーション (800 以上のクラス) があり、分岐され、再構築され、トランクに再統合されています。再統合の前に、標準的な手順であるトランクをブランチにマージしました。

最終的に、ブランチ ソースを含む一連のディレクトリと、トランク ソースを含む一連のディレクトリが作成されました。Beyond Compare を使用して、両方のソース セットが同一であると判断できました。ただし、コンパイル時に ( IntelliJ v11 でホストされている maven を使用する同じ JDK )、約 12 個ほどのクラス ファイルが異なることに気付きました。

明らかに異なるクラス ファイルの各ペアのソースを逆コンパイルすると、同じ Java ソースになりました。したがって、最終結果に関しては、問題ではないようです。しかし、一部のファイルだけが異なっているのはなぜでしょうか?

ありがとう。


追加の考え:

maven/javac が別の順序でファイルをコンパイルすると、最終結果に影響する可能性がありますか?

4

5 に答える 5

9

JDK のバージョン、ビルド ツールのバージョン、およびビルド/コンパイル オプションが同一であると仮定すると、相違点の原因として考えられるものがいくつか考えられます。

  1. タイムスタンプ - クラスファイルには、コンパイルのタイムスタンプが含まれる場合があります。コンパイルをまったく同時に実行しない限り、同じファイルの異なるコンパイルは異なるタイムスタンプになります。

  2. ソース ファイル名のパス - 各クラス ファイルには、ソース ファイルのパス名が含まれます。異なるパス名で 2 つのツリーをコンパイルすると、クラス ファイルには異なるソース パス名が含まれます。

  3. インポートされたコンパイル時定数の値 - クラスAが別のクラスで定義されたコンパイル時定数を使用する場合 (「コンパイル時定数」の定義については JLS を参照)、定数の値はs クラス ファイルBに組み込まれます。そのため、(定数の値が異なる)の異なるバージョンに対してAコンパイルすると、 のコードは異なる可能性があります。ABA

  4. identityHashcodeコンパイラによってキーで使用されることによるHashMap違いは、一部のステップでマップの反復順序の違いにつながる可能性があります。これは.classファイル生成に大きな影響を与えませんが、それでも.classファイルの違いとして現れます。たとえば、一定のプール エントリが異なる順序になる可能性があります。

  5. 外部クラス/メソッドの署名の違い; たとえば、POM ファイルのいずれかで依存関係のバージョンを変更した場合。

  6. 有効なビルド クラスパスが異なると、インポートされたクラスが検出される順序が異なる場合があります。これにより、クラス ファイルの定数プール内のエントリの順序に重要でない違いが生じる可能性があります。これは、次のようなことが原因で発生する可能性があります。

    • 外部 JAR ファイルのディレクトリに異なる順序で表示されるファイル
    • ビルドツールがソースファイルを反復するときにソースファイルの順序が異なるため、ファイルが異なる順序でコンパイルされる2、または
    • ビルドの並列処理 (有効にしている場合)。

ファイルの順序付けに関する問題には回避策があります。JDK-7003006-XDsortfilesで説明されているように、文書化されていないオプションを使用してください。(それについて知ってくれた@Holgerへの称賛。)

通常、ファイル システム ディレクトリ内のファイルの実際の順序は表示されないことに注意してください。lsや などのコマンドライン ツールdir、およびファイル ブラウザは通常、エントリを表示する前に (名前順またはタイムスタンプ順に) 並べ替えます。


1 - これはコンパイラに依存します。javapまた、タイムスタンプが表示されることは保証されていません...それらが存在する場合。

2 - OS は、(syscall レベルで) ディレクトリを一覧表示すると、決定論的な順序でファイル システム オブジェクトが返されるという保証はありません。ファイルを削除して再追加した場合は、同じ順序で返されます。


違いの原因を特定するための最初のステップは、それらが何であるかを正確に理解することであることを付け加えておきます. おそらく難しい方法でそれを行う必要があります-クラスファイルのペアを手動でデコードして、実際に違いがある場所を特定し、違いが実際に何を意味するかを特定します。

于 2012-10-01T12:24:47.667 に答える
2

Beyond Compare を使用して比較すると、ファイルの内容に基づいて比較が行われます。ただし、ビルド プロセスでは、ソース ファイルのタイムスタンプだけが変更されているかどうかがチェックされます。したがって、ソース ファイルの最終変更日が変更されると、再コンパイルされます。

于 2012-10-01T11:21:09.513 に答える
1

コンパイル方法によっては、同じ JDK でも出力が異なる場合があります。デバッグ情報の有無にかかわらずコンパイルできます。古いバージョンで実行するようにコンパイルできます。各オプションは他のクラスになります。

于 2012-10-01T11:22:02.430 に答える
1

Java のバージョンが異なれば、逆コンパイラによって無視されることが多いさまざまなメタデータが追加される可能性があります。

javap -c -vファイル内の詳細については、を使用してみることをお勧めします。これで問題が解決しない場合は、すべてのバイトを調べる ASMifierClassVisitor を使用できます。

于 2012-10-01T11:20:20.017 に答える
1

JDK が異なれば、生成されるバイナリ クラスも異なります (最適化だけでなく、クラスのバージョン番号も異なります)。コンパイル オプションもあります (JDK は古い形式でコンパイルするか、デバッグ情報を追加できます)。

于 2012-10-01T11:18:32.477 に答える