2012-10-01 9 views
11

Kann jemand erklären, wie identische Java-Quellen zu binären unterschiedlichen Klassen-Dateien kompilieren können?Identische Java-Quellen kompilieren zu unterschiedlichen Binärklassen

Die Frage stellt sich aus der folgenden Situation:

Wir haben eine ziemlich große Anwendung (800+ Klassen), die verzweigt wurde, neu strukturiert dann zurück in den Kofferraum reintegriert. Vor der Reintegration haben wir den Stamm in den Zweig integriert, was eine Standardprozedur ist.

Das Endergebnis war eine Reihe von Verzeichnissen mit den Zweigquellen und eine Reihe von Verzeichnissen mit den Stammquellen. Mit Beyond Compare konnten wir feststellen, dass beide Quellen identisch waren. Beim Kompilieren (dasselbe JDK unter Verwendung von Maven in IntelliJ v11) stellten wir jedoch fest, dass ungefähr ein Dutzend der Klassendateien unterschiedlich waren.

Als wir die Quelle für jedes Paar scheinbar unterschiedlicher Klassendateien dekompiliert haben, haben wir am Ende die gleiche Java-Quelle gefunden, was das Endergebnis betrifft, scheint es nicht wichtig zu sein. Aber warum sind nur ein paar Dateien unterschiedlich?

Danke.


Weitere Gedanken:

Wenn Maven/javac Dateien in einer anderen Reihenfolge kompiliert, dass das Endergebnis beeinflussen könnten?

+1

verschiedene jdk-Versionen? Ich kann mir vorstellen, dass die Optimierungen für verschiedene Versionen unterschiedlich sein können. – RNJ

+1

Mit javap -c -v (danke Peter Lawrey) und die jeweiligen Ausgaben mit Beyond Compare (tolles Werkzeug, liebe es!) Ich kann bestätigen, dass Punkt 5 auf Stephen C (Antwort Stephen C) gibt einen Teil der Antwort hier. In einigen Fällen ist die Reihenfolge der konstanten Pools unterschiedlich. Ich bin jedoch ziemlich sicher, dass der Klassenpfad für beide gleich ist, aber die Reihenfolge der Kompilierung kann unterschiedlich sein. – Vicki

Antwort

5

Unter der Annahme, dass die JDKs und Kompilierungsoptionen identisch sind, kann ich mir vorstellen 5 möglich Quellen Unterschiede:

  1. Zeitstempel - jede Klasse Dateikompilierung Zeitstempel enthält. Wenn Sie die Compilations nicht genau zur gleichen Zeit ausführen, haben unterschiedliche Compilations derselben Datei unterschiedliche Zeitstempel.

  2. Quell-Dateipfade - jede Klassendatei enthält den Pfadnamen der Quelldatei. Wenn Sie zwei Bäume mit unterschiedlichen Pfadnamen kompilieren, enthalten die Klassendateien unterschiedliche Quellpfadnamen.

  3. Werte der eingeführten Kompilierung Zeitkonstanten - wenn eine Klasse A eine Kompilierung Zeitkonstante in einer anderen Klasse B (siehe JLS für die Definition eines „Übersetzen Zeitkonstante“), wird der Wert der Konstante eingebaut definierte verwendet wird in A s Klassendatei. Also, wenn Sie A gegen verschiedene Versionen von B kompilieren (mit unterschiedlichen Werten für die Konstanten), wird der Code von A wahrscheinlich anders sein.

  4. Unterschiede in Signaturen externer Klassen/Methoden; z.B. wenn Sie eine Abhängigkeitsversion in einer Ihrer POM-Dateien geändert haben.

  5. Unterschiede in den Klassenpfaden können zu Unterschieden in der Reihenfolge führen, in der importierte Klassen gefunden werden, die zu nicht signifikanten Unterschieden in der Reihenfolge der Einträge im Constant Pool der Klassendatei führen können. Dies könnte aufgrund Dinge passieren wie:

    • Dateien in einer anderen Reihenfolge in den Verzeichnissen von externen JAR-Dateien erscheinen,
    • Dateien in unterschiedlicher Reihenfolge auf die Quelle durch kompilierten Dateien in einer anderen Reihenfolge, wenn die Build-Tool zu sein Iteriert sie oder
    • Parallelität im Build (wenn Sie das aktiviert haben).

Beachten Sie, dass Sie normalerweise nicht die tatsächliche Reihenfolge der Dateien in FS-Verzeichnisse sehen, da Tools wie ls und dir Standard die Einträge zum Sortieren bevor sie angezeigt werden.


sollte ich hinzufügen, dass der erste Schritt, um die Ursache für die Unterschiede zu identifizieren, genau zu arbeiten, was sie sind. Sie müssen wahrscheinlich (müssen) das auf die harte Tour machen - indem Sie manuell ein Paar Klassendateien dekodieren, um die Orte zu identifizieren, wo sie tatsächlich Unterschiede haben ... und was die Unterschiede tatsächlich bedeuten.

+0

Stephen, kann ich nicht sehen Beweise in der Dekompilierung für Punkte 1,2 und Punkte 3 und 4 gelten hier nicht (die Quellen, einschließlich der POMs, sind identisch erinnern). Ich denke jedoch, dass Punkte 5 machbar sind, da die Sequenz, die der Compiler kompiliert, in jedem Fall anders sein kann. – Vicki

+0

Danke Stephen. Ich denke, Punkt 5 und weiter beantwortet meine Frage. – Vicki

1

Verschiedene JDK erzeugen verschiedene Binärklassen (Optimierungen, aber auch Klassenversionsnummer). Es gibt auch Kompilierungsoptionen (ein JDK kompiliert möglicherweise in einem älteren Format oder es kann Debuginformationen hinzufügen).

+0

Ich habe bearbeitet, um die gleichen JDK und Kompilierungsoptionen usw. zu bestätigen. – Vicki

1

Verschiedene Java-Versionen können verschiedene Metadaten hinzufügen, die von einem Decompiler oft ignoriert werden.

Ich schlage vor, Sie versuchen, javap -c -v für weitere Details in einer Datei verwenden. Wenn dies nicht hilft, können Sie den ASMifierClassVisitor verwenden, der jedes Byte untersucht.

2

Wenn Sie Vergleichen ohne Vergleich verwenden, wird der Vergleich basierend auf dem Inhalt der Dateien durchgeführt. Aber im Build-Prozess wird nur der Zeitstempel der Quelldateien auf Änderungen überprüft. Wenn sich das Datum der letzten Änderung Ihrer Quelldatei ändert, wird es neu kompiliert.

+0

Ich hätte klarstellen müssen, dass alle Dateien für beide Sätze von Quellen kompiliert wurden (dh es gab keine Klassendateien vor der Kompilierung) – Vicki

1

dasselbe JDK kann auch unterschiedliche Ausgaben haben, abhängig davon, wie Sie kompilieren. können Sie mit oder ohne Debug-Informationen kompilieren, können Sie kompilieren, um in einer älteren Version zu laufen, jede Option führt zu anderen Klassen.