Can anyone explain how identical Java sources can end up compiling to binary differing class files?
The question arises from the following situation:
<Different versions of Java can add different meta data which is often ignored by a decompiler.
I suggest you try using javap -c -v
for more of the details in a file. If this doesn't help you can use the ASMifierClassVisitor which looks at every byte.
Different JDK produce different binary classes (optimizations, but also class version number). There are compilation options, too (a JDK may compile in an older format, or it can add debug information).
same JDK can also have different output depending on how you compile. you can compile with or without debug info, you can compile to run in an older version, each option will result in other classes.
Assuming that the JDK versions, build tool versions, and build / compilation options are identical, I can still think of a number of possible sources of differences:
Timestamps - class files may1 contain compilation timestamps. Unless you run the compilations at exactly the same times, different compilations of the same file would result different timestamps.
Source filename paths - each class file includes the pathname of the source file. If you compile two trees with different pathnames the class files will contain different source pathnames.
Values of imported compile-time constants - when a class A
uses a compile-time constant defined in another class B
(see JLS for the definition of a "compile time constant"), the value of the constant is incorporated into A
s class file. So if you compile A
against different versions of B
(with different values for the constants), the code of A
is likely to be different.
Differences due to identityHashcode
being used in HashMap
keys by the compiler could lead to differences in map iteration order in some step. This could affect .class
file generation in a way that is not significant, but still shows up as a .class
file difference. For example, constant pool entries could end up in a different order.
Differences in signatures of external classes / methods; e.g. if you changed a dependency version in one of your POM files.
Differences in the effective build classpaths might result in differences in the order in which imported classes are found. This might in turn result in non-significant differences in the order of entries in the class file's Constant Pool. This could happen due to things such as:
There is a possible workaround for the problem with file ordering: use the undocumented -XDsortfiles
option as described in JDK-7003006. (Kudos to @Holger for knowing about that.)
Note that you don't normally see the actual order of files in file system directories. Commandline tools like ls
and dir
, and file browsers will typically sort the entries (in name or timestamp order) before displaying them.
1 - This is compiler dependent. Also, it is not guaranteed that javap
will show the timestamps ... if they are present.
2 - The OS gives no guarantees that listing a directory (at the syscall level) will return the file system objects in a deterministic order ... or the same order, if you have removed and re-added files.
I should add that the first step to identifying the cause of the differences is to work out exactly what they are. You probably need (needed) to do that the hard way - by manually decoding a pair of class files to identify the places where they actually differences ... and what the differences actually mean.
When you compare using beyond compare, comparision is done based on contents of the files. But in the build process just the timestamp of the source files are checked for change. So it your source file's lastmodified date changes it will be recompiled.