2012-10-01 31 views
11

どのようにして同じJavaソースがコンパイルされてバイナリ異なるクラスファイルにコンパイルされるのか説明できますか?同じJavaソースがバイナリ異なるクラスにコンパイル

質問は、以下のような状況から生じる:

私たちは、分枝状されたかなり大規模なアプリケーション(800+クラス)、その後、再構築バックトランクに再統合を持っています。再統合の前に、私たちはトランクをブランチにマージしました。これは標準的な手順です。

最終的な結果は、ブランチソースとトランクソースを持つ一連のディレクトリを持つ一連のディレクトリです。 Beyond Compareを使用すると、両方のソースセットが同一であると判断できました。しかし、IntelliJ v11でホストされているmavenを使用してコンパイルすると(と同じJDK)、クラスファイルが数十程度異なることに気付きました。

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

ありがとうございました。


追加の思考:

のmaven/javacのは、異なる順序でファイルをコンパイルした場合、それが最終結果に影響を与える可能性がありますか?

+1

異なるjdkバージョン?私は最適化がバージョンによって異なるかもしれないと思います。 – RNJ

+1

javap -c -v(感謝Peter Lawrey)とBeyond Compare(偉大なツール、それが大好き!)を使用してそれぞれの出力を見ると、私はStephen Cさん(感謝スティーブンC)の項目5が答えの一部を与えることを確認できます。いくつかのケースでは、プールの順番が異なります。しかし、クラスパスが両方で同じであることは確かですが、コンパイルの順序は異なるかもしれません。 – Vicki

答えて

5

のJDKとコンパイルオプションが同一であると仮定すると、私は違いの5つの可能源と考えることができます:

  1. タイムスタンプ - すべてのクラスファイルは、コンパイルのタイムスタンプが含まれています。コンパイルを正確に同じ時間に実行しない限り、同じファイルの異なるコンパイルでは異なるタイムスタンプが使用されます。

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

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

  5. 外部クラス/メソッドのシグネチャの違い。例えばいずれかのPOMファイルで依存関係のバージョンを変更した場合

  6. ビルドクラスパスの違いにより、インポートされたクラスが見つかる順序が異なるため、クラスファイルの定数プールのエントリの順序に大きな違いが生じないことがあります。

    • 外部JARファイルのディレクトリに異なる順序で表示されるファイルは、
    • によるソース・ファイルが時にビルド・ツール異なる順序であることに異なる順序でコンパイルされたファイル:これは、のようなものに起こることができますそれらを反復するか、またはビルド時に
    • の並列性(有効にしている場合)。

あなたが正常にそれらを表示する前にエントリをソートするlsdirデフォルトのようなツールので、FSディレクトリ内のファイルの実際の順序は表示されません。


私は、違いの原因を特定するための最初のステップは、それらが何であるかを正確に調べることだと付け加えておきます。実際に違いのある場所を特定するためにクラスファイルのペアを手動でデコードすることによって、そしてその違いが実際にどういう意味を持つか、という難しい方法が必要になるでしょう。

+0

Stephen、アイテム1,2およびアイテム3および4の逆コンパイルの証拠はここでは適用されません(POMを含むソースは同じです)。しかし、コンパイラがコンパイルするシーケンスはどちらの場合も異なる可能性があるため、項目5は実現可能であると私は思う。 – Vicki

+0

スティーブンに感謝します。私はアイテム5以降が私の質問に答えると思う。 – Vicki

1

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

+0

同じJDKとコンパイルオプションなどを確認するために編集しました – Vicki

1

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

ファイル内の詳細については、javap -c -vをお試しください。これが役に立たない場合は、すべてのバイトを調べるASMifierClassVisitorを使用できます。

2

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

+0

すべてのファイルが両方のソースセットでコンパイルされていることを明確にしておく必要があります(コンパイル前にクラスファイルが存在しませんでした) – Vicki

1

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

関連する問題