2009-03-26 10 views
8

Tengo un proyecto Java Maven con aproximadamente 800 archivos fuente (algunos generados por javacc/JTB) que demoran unos buenos 25 minutos en compilarse con javac.¿Por qué javac 1.5 se ejecuta tan lentamente comparado con el compilador Eclipse?

Cuando cambié mi pom.xml para utilizar el compilador Eclipse, tarda unos 30 segundos en compilarse.

¿Alguna sugerencia de por qué javac (1.5) se ejecuta tan lentamente? (No quiero cambiar al compilador de Eclipse permanentemente, ya que el plugin de Maven parece más que un pequeño error).

Tengo un caso de prueba que reproduce fácilmente el problema. El siguiente código genera una cantidad de archivos fuente en el paquete predeterminado. Si intenta compilar ImplementingClass.java con javac, parecerá que se detiene por un tiempo desorbitado.

import java.io.File; 
import java.io.FileNotFoundException; 
import java.io.PrintStream; 

public class CodeGenerator 
{ 
    private final static String PATH = System.getProperty("java.io.tmpdir"); 
    private final static int NUM_TYPES = 1000; 

    public static void main(String[] args) throws FileNotFoundException 
    { 
     PrintStream interfacePs = new PrintStream(PATH + File.separator + "Interface.java"); 
     PrintStream abstractClassPs = new PrintStream(PATH + File.separator + "AbstractClass.java"); 
     PrintStream implementingClassPs = new PrintStream(PATH + File.separator + "ImplementingClass.java"); 
     interfacePs.println("public interface Interface<T> {"); 
     abstractClassPs.println("public abstract class AbstractClass<T> implements Interface<T> {"); 
     implementingClassPs.println("public class ImplementingClass extends AbstractClass<Object> {"); 

     for (int i=0; i<NUM_TYPES; i++) 
     { 
      String nodeName = "Node" + i; 
      PrintStream nodePs = new PrintStream(PATH + File.separator + nodeName + ".java"); 
      nodePs.printf("public class %s { }\n", nodeName); 
      nodePs.close(); 
      interfacePs.printf("void visit(%s node, T obj);%n", nodeName); 
      abstractClassPs.printf("public void visit(%s node, T obj) { System.out.println(obj.toString()); }%n", nodeName); 
     } 
     interfacePs.println("}"); 
     abstractClassPs.println("}"); 
     implementingClassPs.println("}"); 
     interfacePs.close(); 
     abstractClassPs.close(); 
     implementingClassPs.close(); 
    } 
} 
+1

Pruebe a producir un SSCCE (http://sscce.org/) y presente un informe de error con Sun (http://bugs.sun.com/). Especialmente porque ya redujo los problemas a un caso bastante específico. –

+0

¿Qué sistema operativo está usando? Es rápido para mí ... en OS X. – TofuBeer

+0

Esto es en Windows XP y Windows Server 2003 –

Respuesta

6

Obtiene el mismo comportamiento con JDK 1.6, incluida la actualización 14, compilación 04, el uso de G1 no cambia el comportamiento (aunque G1 parece funcionar muy bien).

javac Monitoreo con jvisualvm, vertederos de rosca repetidas muestran que el hilo principal de pasar mucho tiempo en

at com.sun.tools.javac.code.Types.isSubSignature(Types.java:1846) 
at com.sun.tools.javac.code.Symbol$MethodSymbol.overrides(Symbol.java:1108) 
at com.sun.tools.javac.code.Symbol$MethodSymbol.implementation(Symbol.java:1159) 
at com.sun.tools.javac.comp.Check.checkCompatibleConcretes(Check.java:1239) 
at com.sun.tools.javac.comp.Check.checkCompatibleSupertypes(Check.java:1567) 
at com.sun.tools.javac.comp.Attr.attribClassBody(Attr.java:2674) 
at com.sun.tools.javac.comp.Attr.attribClass(Attr.java:2628) 
at com.sun.tools.javac.comp.Attr.attribClass(Attr.java:2564) 
at com.sun.tools.javac.main.JavaCompiler.attribute(JavaCompiler.java:1036) 
at com.sun.tools.javac.main.JavaCompiler.compile2(JavaCompiler.java:765) 
at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:730) 
at com.sun.tools.javac.main.Main.compile(Main.java:353) 
at com.sun.tools.javac.main.Main.compile(Main.java:279) 
at com.sun.tools.javac.main.Main.compile(Main.java:270) 
at com.sun.tools.javac.Main.compile(Main.java:69) 
at com.sun.tools.javac.Main.main(Main.java:54) 

y batiendo a través de un gran número de casos de breve duración de estas clases:

com.sun.tools.javac.code.Types$Subst 
com.sun.tools.javac.util.List 
com.sun.tools.javac.code.Types$MethodType 

I sospecha que el código se está agitando a través de com.sun.tools.javac.comp.Check.checkCompatibleConcretes comparando cada método con cualquier otro método

javadoc de ese método:

/** Check that a class does not inherit two concrete methods 
* with the same signature. 
*/ 

Es posible que el compilador de eclipse no realice esa comprobación o no la realice de la misma manera.

0

Quizás la compilación de Eclipse compila solo fuentes modificadas. ¿Qué sucede si lo compila en eclipse después de una limpieza?

+0

Hice un 'mvn clean' antes de 'mvn install' en ambos casos. –

1

Para el compilador de Sun está iniciando un proceso de JVM completo para cada archivo que desea compilar. Para el compilador de Eclipse, solo se está conectando a un proceso de daemon. Sugiero configurar el tenedor en falso, aunque puede que no sea tan rápido.

+0

¿Eso haría una diferencia de 25 minutos? No lo creo ... –

+0

25 minutos/80 archivos de fuentes = 18.75 segundos por javac. Posible si para cada archivo fuente tuviera que cargar una carga de jarras/clases. –

+0

Gracias por su sugerencia. Lo probé, pero desafortunadamente no marcó una diferencia notable en el tiempo de compilación. –

2

El hecho de que usted está utilizando fuente generado, la diferencia masiva en la velocidad y la StackOverflowError podrían sugerir que una (o más) de los archivos tienen algunas construcciones que los analizadores javac no está de acuerdo con.

Podría tratar de compilar solo subconjuntos de su código y ver si una clase/paquete ralentiza especialmente el proceso (probablemente uno de los generados).

+0

Ahora he intentado dividir el proyecto en dos: 391 archivos generados por javacc/jtb y 373 codificados a mano. La gran mayoría del tiempo se gasta en compilar los codificados a mano (compilar los generados tomó aproximadamente 18 segundos) –

+0

Interesante, no lo habría adivinado. Me gustaría dividir los archivos de origen cada vez más para tratar de averiguar si algunos archivos son responsables de la desaceleración. –

+0

Otra sugerencia: ejecuta tu compilación usando "strace -eopen hormiga". Esto se imprimirá cada vez que se abra un archivo. Espere a que haya pausas en esa gran cantidad de salida y compruebe el último nombre de archivo abierto anteriormente. Debería darte una pista decente. –

5

Es posible que el compilador javac funcione cerca de su límite de almacenamiento dinámico (64MB o menos). En ese caso, pasa la mayor parte del tiempo en el recolector de basura. Dele al compilador una buena cantidad de memoria, digamos 256M o 512M y vea si funciona más rápido.

+0

A menos que se configure de otra manera, el plugin maven-compiler ejecuta javac en proceso. Una posible solución para simonn intentar sería establecer la variable de entorno MAVEN_OPTS en "-Xms128M Xmx512M" más o menos. Si el complemento está configurado fork = true, puede usar los parámetros meminitial y maxmem para controlar esto. – Barend

+0

He intentado ejecutar "javac -verbose -J-Xms512m -J-Xmx1024m ImplementingClass.java", y el problema persiste. –

0

No sé cómo maven llama al compilador pero las cifras de rendimiento que menciona sugieren que javac se ejecuta en su propio proceso/VM como ya se sugirió en otra respuesta. Como comenzar un nuevo proceso/máquina virtual para cada archivo que compila es muy costoso, debe asegurarse de configurar el compilador para usar la máquina virtual que ya podría tener. Sé que ANT ofrece eso, pero yo no he usado maven yo mismo. Dado el hecho de que es popular, dudo que carezca de una característica tan importante.

0

creo que algo como lo siguiente está pasando: Maven horquillas javac, procesos JVM para las etapas separadas en su ciclo de vida: Maven Build Life-cycle

Eclipse normalmente se ejecuta su compilación en el fondo (en Save), de modo que el paso se agregará a la fase de compilación. Si hay dependencias sustanciales, aquí es donde está perdiendo rendimiento.

Además (dependiendo de la configuración mvn) cada método de prueba obtiene su propia JVM. Como el paso de la prueba es una prerrequisito para la fase del paquete, es posible que esté perdiendo tiempo con las ejecuciones de la prueba JUnit (especialmente si son pruebas de ejecución lenta). Este es solo un posible culpable si tienes un montón de código de prueba en tu árbol fuente.

Lo más probable es que su clase tenga cantidades considerables de E/S de archivos, por lo que es un área de oportunidad. Parece que su ciclo se está ejecutando 1000 veces por evento de descubrimiento de archivos, lo que significa 800 * 1000 = 800,000 creaciones de PrintStream en el cuerpo del ciclo.

7

Sun me ha confirmado por correo electrónico que este es un nuevo error (6827648 en su base de datos de errores).

Cuestiones relacionadas