95

同じ JDK (つまり、同じ実行可能ファイル)を使用する場合javac、生成されるクラス ファイルは常に同じですか? オペレーティング システムまたはハードウェアによって違いはありますか? JDK のバージョン以外に、違いを生む要因はありますか? 違いを避けるためのコンパイラ オプションはありますか? 違いは理論上の可能性だけですか、それとも Oracle はjavac実際には同じ入力およびコンパイラ オプションに対して異なるクラス ファイルを生成しますか?

Update 1クラスファイルがさまざまなプラットフォームで実行できるかどうかではなく、生成、つまりコンパイラ出力に興味があります。

更新 2「同じ JDK」とは、同じjavac実行可能ファイルも意味します。

Update 3 Oracle のコンパイラにおける理論上の違いと実際的な違いの違い。

[編集、言い換えた質問を追加]
「同じ javac 実行可能ファイルを別のプラットフォームで実行すると、別のバイトコードが生成されるのはどのような状況ですか?」

4

11 に答える 11

39

コンパイラが各プラットフォームで同じバイトコードを生成する義務はありません。javac具体的な答えを得るには、さまざまなベンダーのユーティリティを参照する必要があります。


このための実際的な例をファイルの順序付けで示します。

2つのjarファイルがあるとしましょう:my1.jarMy2.jar。それらはlib並べてディレクトリに配置されます。コンパイラはそれらをアルファベット順に読み取りますが(これはlib)ですが、ファイルシステムで大文字と小文字が区別されない場合はmy1.jar、順序は大文字と小文字が区別される場合はです。My2.jarMy2.jarmy1.jar

には メソッドをmy1.jar持つクラスがありますA.class

public class A {
     public static void a(String s) {}
}

My2.jar同じA.classですが、メソッドシグネチャが異なります(accepts Object):

public class A {
     public static void a(Object o) {}
}

電話があれば明らかです

String s = "x"; 
A.a(s); 

さまざまなケースでさまざまなシグネチャを使用してメソッド呼び出しをコンパイルします。したがって、ファイルシステムのケースの機密性に応じて、結果として異なるクラスを取得します。

于 2013-02-20T16:43:15.733 に答える
6

短い答え-いいえ


長い答え

bytecode異なるプラットフォームで同じである必要はありません。バイトコードを正確に実行する方法を知っているのはJRE(Javaランタイム環境)です。

Java VM仕様を確認すると、バイトコードが異なるプラットフォームで同じであるということは、これが真実である必要はないことがわかります。

クラスファイル形式を確認すると、クラスファイルの構造が次のように表示されます。

ClassFile {
    u4 magic;
    u2 minor_version;
    u2 major_version;
    u2 constant_pool_count;
    cp_info constant_pool[constant_pool_count-1];
    u2 access_flags;
    u2 this_class;
    u2 super_class;
    u2 interfaces_count;
    u2 interfaces[interfaces_count];
    u2 fields_count;
    field_info fields[fields_count];
    u2 methods_count;
    method_info methods[methods_count];
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}

マイナーバージョンとメジャーバージョンについて確認する

minor_version、major_version

minor_version項目とmajor_version項目の値は、このクラスファイルのマイナーバージョン番号とメジャーバージョン番号です。メジャーバージョン番号とマイナーバージョン番号を合わせて、クラスファイル形式のバージョンを決定します。クラスファイルのメジャーバージョン番号がMでマイナーバージョン番号がmの場合、そのクラスファイル形式のバージョンをMmと表記します。したがって、クラスファイル形式のバージョンは辞書式順序で並べることができます(例:1.5 <2.0 <2.1)。Java仮想マシンの実装は、vがMi.0 v Mj.mの連続した範囲にある場合にのみ、バージョンvのクラスファイル形式をサポートできます。Sunだけが、Javaプラットフォームの特定のリリースレベルに準拠するJava仮想マシンの実装がサポートできるバージョンの範囲を指定できます。1

脚注でもっと読む

1 SunのJDKリリース1.0.2のJava仮想マシンの実装は、クラスファイル形式バージョン45.0から45.3までをサポートします。SunのJDKリリース1.1.Xは、45.0から45.65535までの範囲のバージョンのクラスファイル形式をサポートできます。Java 2プラットフォームのバージョン1.2の実装は、45.0から46.0までの範囲のバージョンのクラスファイル形式をサポートできます。

したがって、これらすべてを調査すると、異なるプラットフォームで生成されたクラスファイルが同一である必要はないことがわかります。

于 2013-02-20T16:44:56.413 に答える
3

まず、仕様にはそのような保証はまったくありません。準拠するコンパイラは、生成されたクラス ファイルに追加の (カスタム) 属性としてコンパイル時刻をスタンプできますが、クラス ファイルは依然として正しいものです。ただし、ビルドごとにバイトレベルの異なるファイルが生成されます。

第二に、そのような厄介なトリックがなくても、構成と入力の両方が2つのケースで同一でない限り、コンパイラがまったく同じことを2回続けて行うことを期待する理由はありません. 仕様では、ソース ファイル名が標準属性の 1 つとして記述されており、ソース ファイルに空白行を追加すると、行番号テーブルが変更される可能性があります。

第三に、ホストプラットフォームによるビルドの違いに遭遇したことはありません(クラスパス上の違いに起因するものを除く)。プラットフォームによって異なるコード (つまり、ネイティブ コード ライブラリ) はクラス ファイルの一部ではなく、バイトコードからのネイティブ コードの実際の生成は、クラスがロードされた後に行われます。

第 4 に (そして最も重要なことに) これを知りたいと思うと、プロセスの悪臭(コードのにおいのようなものですが、コードに対してどのように行動するか) のにおいがします。可能であれば、ビルドではなくソースにバージョンを付けます。ビルドにバージョンを付ける必要がある場合は、個々のクラス ファイルではなく、コンポーネント レベル全体でバージョンを付けます。優先的に、CI サーバー (Jenkins など) を使用して、ソースを実行可能なコードに変換するプロセスを管理します。

于 2013-02-20T22:51:57.713 に答える
2

同じJDKを使用する場合、生成されるバイトコードは、使用するハードウェアやOSとは関係なく、常に同じになると思います。バイトコードの生成は、決定論的アルゴリズムを使用してソースコードをバイトコードに「変換」するJavaコンパイラによって行われます。したがって、出力は常に同じになります。このような状況では、ソースコードの更新のみが出力に影響します。

于 2013-02-20T16:45:02.803 に答える
1

全体として、同じコンパイラで異なるプラットフォーム上でコンパイルした場合、同じソースが同じバイトコードを生成するという保証はないと言わざるを得ません。

日本語をサポートする Windows など、さまざまな言語 (コード ページ) が関係するシナリオを検討します。マルチバイト文字を考えてください。コンパイラが常にすべての言語をサポートする必要があると想定しない限り、8 ビット ASCII 用に最適化する可能性があります。

Java 言語仕様にバイナリ互換性に関するセクションがあります。

SOM におけるリリースからリリースへのバイナリ互換性 (Forman、Conner、Danforth、および Raper による OOPSLA '95 の議事録) のフレームワーク内では、Java プログラミング言語のバイナリは、作成者が特定したすべての関連する変換の下でバイナリ互換性があります (ただし、いくつかの警告があります)。インスタンス変数の追加に関して)。それらのスキームを使用して、Java プログラミング言語がサポートする重要なバイナリ互換の変更のリストを次に示します。

•既存のメソッド、コンストラクタ、イニシャライザを再実装してパフォーマンスを向上させます。

•メソッドまたはコンストラクターを変更して、以前は通常発生しないはずの例外をスローしたか、無限ループに入るかデッドロックを引き起こしたために失敗した入力に対して値を返します。

•新しいフィールド、メソッド、またはコンストラクターを既存のクラスまたはインターフェースに追加する。

• クラスのプライベート フィールド、メソッド、またはコンストラクタを削除する。

•パッケージ全体が更新された場合、デフォルト (パッケージのみ) のアクセス フィールド、メソッド、またはパッケージ内のクラスとインターフェイスのコンストラクターを削除します。

•既存の型宣言内のフィールド、メソッド、またはコンストラクターの並べ替え。

•クラス階層でメソッドを上に移動します。

クラスまたはインターフェースの直接スーパーインターフェースのリストを並べ替える。

•型階層に新しいクラスまたはインターフェース型を挿入する。

この章では、すべての実装によって保証されるバイナリ互換性の最低基準を指定します。Java プログラミング言語は、互換性のあるソースからのものであることが知られていないが、ここで説明する互換性のある方法でソースが変更されているクラスとインターフェースのバイナリが混在している場合、互換性を保証します。アプリケーションのリリース間の互換性について説明していることに注意してください。Java SE プラットフォームのリリース間の互換性については、この章の範囲外です。

于 2013-02-20T18:05:11.983 に答える
1

Java allows you write/compile code on one platform and run on different platform. 知る限り; これは、異なるプラットフォームで生成されたクラス ファイルが同じか、技術的に同じ、つまり同一である場合にのみ可能です。

編集

技術的に同じコメントが意味することは、それです。バイトごとに比較する場合、完全に同じである必要はありません。

したがって、仕様に従って、異なるプラットフォーム上のクラスの .class ファイルは、バイトごとに一致する必要はありません。

于 2013-02-20T16:48:30.563 に答える
1

質問について:

「同じ javac 実行可能ファイルを別のプラットフォームで実行すると、別のバイトコードが生成されるのはどのような状況ですか?」

クロスコンパイルの例は、Javac オプションの使用方法を示しています:-target version

このフラグは、このコマンドの呼び出し時に指定した Java バージョンと互換性のあるクラス ファイルを生成します。したがって、クラス ファイルは、このオプションを使用してコンパイル中に提供する属性によって異なります。

于 2013-02-27T09:30:51.730 に答える
0

ほとんどの場合、答えは「はい」ですが、正確な答えを得るには、コンパイル中にいくつかのキーまたはGUID生成を検索する必要があります。

これが起こった状況を思い出せません。たとえば、シリアル化の目的でIDを取得するには、ハードコーディングされます。つまり、プログラマーまたはIDEによって生成されます。

PSまた、JNIも重要です。

PPSjavacそれ自体がJavaで書かれていることがわかりました。これは、異なるプラットフォームで同一であることを意味します。したがって、理由がなければ別のコードを生成することはありません。したがって、これはネイティブ呼び出しでのみ実行できます。

于 2013-02-20T16:43:32.483 に答える
0

2 つの質問があります。

Can there be a difference depending on the operating system or hardware? 

これは理論的な質問であり、答えは明らかに「はい、あり得る」です。他の人が言ったように、仕様では、コンパイラがバイト単位で同一のクラス ファイルを生成する必要はありません。

現在存在するすべてのコンパイラがすべての状況 (異なるハードウェアなど) で同じバイト コードを生成したとしても、明日の答えは異なる可能性があります。javac またはオペレーティング システムを更新する予定がない場合は、特定の状況でそのバージョンの動作をテストできますが、たとえば Java 7 Update 11 から Java 7 Update 15 に移行すると、結果が異なる場合があります。

What are the circumstances where the same javac executable, when run on a different platform, will produce different bytecode?

それはわかりません。

構成管理が質問をする理由かどうかはわかりませんが、気にするのは理解できる理由です。バイト コードを比較することは IT 部門の正当な管理ですが、ソース ファイルが変更されたかどうかを判断するためではなく、クラス ファイルが変更されたかどうかを判断するためだけです。

于 2013-02-27T13:33:40.303 に答える
0

別の言い方をします。

まず、問題は決定論的であることではないと思います。

もちろん、これは決定論的です。コンピュータ サイエンスではランダム性を実現するのは難しく、コンパイラがここでランダム性を導入する理由はありません。

第二に、「同じソースコード ファイルのバイトコード ファイルはどれくらい似ているか?」で再定式化すると、いいえ、それらが似ているという事実に頼ることはできません。

これを確認する良い方法は、git ステージに .class (または私の場合は .pyc) を残すことです。チーム内のさまざまなコンピューター間で、.py ファイルに変更が加えられていない場合 (および .pyc が再コンパイルされた場合) に、git が .pyc ファイル間の変更に気付くことがわかります。

少なくともそれは私が観察したことです。*.pyc と *.class を .gitignore に入れてください!

于 2013-02-27T10:10:59.680 に答える