2012-10-01 14 views
11

¿Alguien puede explicar cómo fuentes idénticas de Java pueden terminar compilando a binario archivos de clase diferentes?Fuentes idénticas de Java compilan en clases binarias diferentes

La pregunta surge de la siguiente situación:

Tenemos una aplicación bastante grande (más de 800 clases) que se ha ramificado, reestructurada luego reintegrado en el tronco. Antes de la reintegración, fusionamos el tronco en la rama, que es el procedimiento estándar.

El resultado final fue un conjunto de directorios con las fuentes de la sucursal y un conjunto de directorios con las fuentes troncales. Utilizando Beyond Compare pudimos determinar que ambos conjuntos de fuentes eran idénticos. Sin embargo, al compilar (mismo JDK usando maven hospedado en IntelliJ v11) notamos que aproximadamente una docena de los archivos de clase eran diferentes.

Cuando descompilamos la fuente de cada par de archivos de clase aparentemente diferentes, terminamos con la misma fuente de Java, por lo que en términos del resultado final, no parece importar. ¿Pero por qué es que solo algunos de los archivos son diferentes?

Gracias.


pensamiento adicional:

Si experto/javac compila archivos en una secuencia diferente, puede que afectará el resultado final?

+1

diferentes versiones jdk? Me imagino que las optimizaciones pueden ser diferentes para las diferentes versiones. – RNJ

+1

Usando javap -c -v (gracias Peter Lawrey) y viendo las salidas respectivas usando Beyond Compare (gran herramienta, ¡me encanta!) Puedo confirmar que el ítem 5 de la respuesta de Stephen C (gracias a Stephen C) da parte de la respuesta aquí. En varios de los casos, el orden de grupo constante es diferente. Sin embargo, estoy bastante seguro de que classpath es el mismo para ambos, pero el orden de compilación puede ser diferente. – Vicki

Respuesta

5

Suponiendo que los JDK y opciones de compilación son idénticos, lo que puedo pensar de 5 posibles fuentes de diferencias:

  1. marcas de tiempo - cada archivo de clase contiene las marcas de tiempo de compilación. A menos que ejecute las compilaciones exactamente en el mismo momento, las diferentes compilaciones del mismo archivo tendrán diferentes marcas de tiempo.

  2. Ruta del nombre de archivo de origen: cada archivo de clase incluye la ruta de acceso del archivo fuente. Si compila dos árboles con diferentes nombres de ruta, los archivos de clase contendrán diferentes nombres de ruta de origen.

  3. valores de las constantes de tiempo de compilación importados - cuando una clase A utiliza una constante de tiempo de compilación se define en otra clase B (ver JLS para la definición de un "compilar constante de tiempo"), se incorpora el valor de la constante en el archivo de clase A s. Por lo tanto, si compila A con diferentes versiones de B (con diferentes valores para las constantes), es probable que el código de A sea diferente.

  4. Diferencias en firmas de clases/métodos externos; p.ej. si cambiaste una versión de dependencia en uno de tus archivos POM.

  5. Las diferencias en los classpaths de compilación pueden dar lugar a diferencias en el orden en que se encuentran las clases importadas que pueden dar lugar a diferencias no significativas en el orden de las entradas en el conjunto de constantes del archivo de clase. Esto podría suceder debido a cosas tales como:

    • archivos que aparecen en diferente orden en los directorios de archivos JAR externos,
    • archivos que se compilan en un orden diferente debido a los archivos de origen estar en orden diferente cuando su herramienta de construcción los itera, o
    • paralelismo en la compilación (si tiene eso habilitado).

Tenga en cuenta que normalmente no ve el orden real de los archivos en los directorios FS, debido a herramientas como ls y dir por defecto a la clasificación de las entradas antes de mostrarlos.


Debo añadir que el primer paso para identificar la causa de las diferencias es encontrar exactamente lo que son. Probablemente necesites (necesites) hacerlo de la manera difícil: decodificando manualmente un par de archivos de clase para identificar los lugares donde realmente difieren ... y qué significan realmente las diferencias.

+0

Stephen, no veo ningún archivo la evidencia en la descompilación para los ítems 1, 2 y los ítems 3 y 4 no se aplican aquí (las fuentes, incluidos los POM, son idénticas). Sin embargo, creo que los elementos 5 son factibles, ya que la secuencia que compila el compilador puede ser diferente en ambos casos. – Vicki

+0

Gracias Stephen. Creo que el ítem 5 en adelante responde mi pregunta. – Vicki

1

Diferentes JDK producen diferentes clases binarias (optimizaciones, pero también número de versión de clase). También hay opciones de compilación (un JDK puede compilarse en un formato anterior o puede agregar información de depuración).

+0

He editado para confirmar el mismo JDK y las mismas opciones de compilación, etc. – Vicki

1

Las diferentes versiones de Java pueden agregar metadatos diferentes que a menudo son ignorados por un decompilador.

Le sugiero que intente utilizar javap -c -v para obtener más información sobre un archivo. Si esto no ayuda, puede usar el ASMifierClassVisitor que examina cada byte.

2

Al comparar usando más allá de la comparación, la comparación se realiza en función del contenido de los archivos. Pero en el proceso de compilación solo se verifica la modificación de la marca de tiempo de los archivos fuente. Por lo tanto, la fecha de última modificación de su archivo de origen se volverá a compilar.

+0

Debería haber aclarado que todos los archivos fueron compilados para ambos grupos de fuentes (es decir, no había archivos de clase existentes antes de la compilación) – Vicki

1

mismo JDK también puede tener una salida diferente según cómo se compile. puede compilar con o sin información de depuración, puede compilar para ejecutar en una versión anterior, cada opción dará como resultado otras clases.

Cuestiones relacionadas