为什么与Eclipse编译器相比,javac 1.5的运行速度如此之慢?

我有一个Java Maven项目,包含大约800个源文件(其中一些是由javacc / JTB生成的),用javac编译需要25分钟。

当我将pom.xml更改为使用Eclipse编译器时,编译大约需要30秒。

关于为什么javac(1.5)运行如此缓慢的任何建议? (我不想永久切换到Eclipse编译器,因为Maven的插件似乎不仅仅是一个小错误。)

我有一个测试用例,很容易重现问题。 以下代码在默认包中生成许多源文件。 如果您尝试使用javac编译ImplementingClass.java,它似乎会暂停很长时间。

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 {"); abstractClassPs.println("public abstract class AbstractClass implements Interface {"); implementingClassPs.println("public class ImplementingClass extends AbstractClass {"); 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(); } } 

使用JDK 1.6获得相同的行为,包括更新14,构建04,使用G1不会改变行为,(尽管G1看起来效果很好)。

使用jvisualvm监视javac,重复的线程转储显示主线程花费大量时间

 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) 

并通过这些类的大量短期实例进行搅拌:

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

我怀疑代码是通过com.sun.tools.javac.comp.Check.checkCompatibleConcretes比较每个方法与其他所有方法

那个方法是javadoc:

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

可能是eclipse的编译器要么不执行该检查,要么不以相同的方式执行它。

Sun通过电子邮件向我确认这是一个新的错误(他们的bug数据库中有6827648 )。

可能是javac编译器在其堆限制(64MB左右)附近运行。 在这种情况下,它大部分时间都花在垃圾收集器上。 给编译器一个很好的内存块,比如256M或512M,看它是否运行得更快。

您使用生成的源,速度和StackOverflowError巨大差异这一事实可能表明您的一个(或多个)文件具有javac解析器不同意的一些构造。

您是否可以尝试仅编译代码的子集,并查看是否有任何一个类/包特别减慢了进程(可能是生成的一个)。

对于Sun编译器,您要为要编译的每个文件启动整个JVM进程。 对于Eclipse编译器,它只是连接到守护进程。 我建议将fork设置为false,尽管它可能不会那么快。

Eclipse构建可能只是编译修改后的源代码。 如果你在干净后在eclipse中编译它会发生什么?

我不知道maven如何调用编译器,但你提到的性能数字表明javac是在它自己的进程/ VM中执行的,正如另一个答案中已经建议的那样。 为每个编译的文件启动一个新的进程/ VM是非常昂贵的,您需要确保配置编译器以使用您可能已经拥有的VM。 我知道ANT提供的,但我自己没有使用过maven。 鉴于它很受欢迎,我怀疑它缺乏如此重要的function。

我认为以下内容正在发生:Maven分支javac,JVM进程在其生命周期中的单独步骤: Maven构建生命周期

Eclipse通常在后台运行其编译(在Save上),因此该步骤将添加到编译阶段。 如果存在实质性依赖关系,那么这就是您丢失吞吐量的地方。

另外(取决于mvn配置)每个测试方法都有自己的JVM。 由于测试段落是封装阶段的预先要求,因此您可能会浪费时间进行JUnit测试执行(特别是如果测试执行速度慢)。 如果你的源代码树中有很多测试代码,这只是一个可能的罪魁祸首。

最有可能的是,您的class级会进行大量的文件I / O,因此这是一个机会领域。 看起来你的循环每个文件发现事件执行1000次,这意味着循环体中有800 * 1000 = 800,000个PrintStream创建。