web-dev-qa-db-ja.com

Javaクラスファイルの作成は決定的ですか?

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

pdate 1generationに興味があります。つまり、クラスファイルがrunさまざまなプラットフォームで。

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

pdate Oracleコンパイラの理論上の違いと実際の違いの違い。

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

94
mstrap

このようにしましょう:

同じ.classファイルが与えられた場合、同じ.Javaファイルを2回生成することのない完全準拠のJavaコンパイラーを簡単に生成できます。

これを行うには、あらゆる種類のバイトコード構成を微調整するか、単にメソッドに余分な属性を追加します(許可されています)。

specificationは、コンパイラがバイト単位の同一クラスファイルを生成することをrequireしないことを考えると、そのような結果に依存しないようにします

ただし、、確認した数回は、同じスイッチ(および同じライブラリ!)を使用して同じコンパイラで同じソースファイルをコンパイルしました didは、同じ.classファイルになります。

更新:最近つまずいたのは Java 7のswitchStringの実装に関するこの興味深いブログ投稿 です。このブログ投稿には、関連する部分がいくつかありますので、ここで引用します(強調する):

コンパイラの出力を予測可能および繰り返し可能にするために、これらのデータ構造で使用されるマップとセットは、単にLinkedHashMapLinkedHashSetではなく、HashMapssとHashSetssです。 特定のコンパイル中に生成されたコードの機能的正確性の観点からHashMapおよびHashSetを使用することは問題ありません;反復順序は関係ありません。ただし、javacの出力がシステムクラスの実装の詳細に基づいて変化しないことが有益であることがわかります

これは問題をかなり明確に示しています:コンパイラーは、仕様に一致する限り、決定論的な方法で動作することは不要です。しかし、コンパイラ開発者は、try一般的に良いアイデアであることを認識します(おそらく、あまり高価ではないという条件で)。

68
Joachim Sauer

コンパイラが各プラットフォームで同じバイトコードを生成する義務はありません。特定の回答については、さまざまなベンダーのjavacユーティリティを参照してください。


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

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

my1.jarにはクラスがありますA.classメソッド付き

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

My2.jarは同じA.class、ただし、異なるメソッドシグネチャ(Objectを受け入れます):

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

通話がある場合は明らかです

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

さまざまな場合に異なるシグネチャでメソッド呼び出しをコンパイルします。 ファイルシステムの大文字と小文字の区別に応じて、結果として異なるクラスが得られます。

38
gaborsch

短い答え-[〜#〜] no [〜#〜]


ロングアンサー

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

Java VM specification を通過すると、バイトコードが異なるプラットフォームで同じであるということは、必ずしもそうである必要はないことがわかります。

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

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がある場合、そのクラスファイル形式のバージョンをM.mとして示します。したがって、クラスファイル形式のバージョンは、辞書式に、たとえば1.5 <2.0 <2.1のように並べられます。 A Java仮想マシンの実装は、vが連続する範囲Mi.0 v Mj.mにある場合にのみ、バージョンvのクラスファイル形式をサポートできます。 Java特定のリリースレベルに準拠する仮想マシンの実装Javaプラットフォームがサポートする場合があります。1

脚注をもっと読む

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

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

6
mtk

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

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

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

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

3
Donal Fellows

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

2
viniciusjssouza

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

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

Java Language Specification にバイナリ互換性に関するセクションがあります。

SOMのRelease-to-Release Binary Compatibilityのフレームワーク内(Forman、Conner、Danforth、およびRaper、Proceedings of OOPSLA '95)では、Javaプログラミング言語のバイナリは、関連するすべての変換でバイナリ互換です著者が特定する(インスタンス変数の追加に関していくつかの注意事項があります。)スキームを使用して、Javaプログラミング言語がサポートする重要なバイナリ互換の変更のリストを以下に示します。

•既存のメソッド、コンストラクター、および初期化子を再実装して、パフォーマンスを改善します。

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

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

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

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

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

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

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

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

この章では、すべての実装で保証されるバイナリ互換性の最小標準を指定します。 Javaプログラミング言語は、互換性のあるソースからのものではないが、ソースがここで説明する互換性のある方法で変更されたクラスとインターフェースのバイナリが混在する場合の互換性を保証します。 Java SEプラットフォームのリリース間の互換性に関する議論は、この章の範囲外です。

1
Kelly S. French

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

編集

技術的に同じコメントとはどういう意味ですか。バイトごとに比較する場合、それらは正確に同じである必要はありません。

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

1
rai.skumar

質問について:

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

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

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

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

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

追伸JNIも重要です。

P.P.S. javac自体がJavaで書かれていることがわかりました。これは、異なるプラットフォームで同一であることを意味します。したがって、理由なく異なるコードを生成することはありません。そのため、ネイティブコールでのみこれを行うことができます。

0
Suzan Cioc

2つの質問があります。

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

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

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

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

それはわかりません。

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

0
Skip Addison

別の言い方をします。

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

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

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

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

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

0