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

时间:2009-03-26 16:32:06

标签: java eclipse maven-2 javac

我有一个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<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();
    }
}

8 个答案:

答案 0 :(得分:7)

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

答案 1 :(得分:6)

使用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的编译器不执行该检查,或者不以相同的方式执行它。

答案 2 :(得分:5)

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

答案 3 :(得分:2)

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

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

答案 4 :(得分:1)

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

我认为以下内容正在发生:Maven分支javac,JVM处理其生命周期中的单独步骤:Maven Build Life-cycle

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

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

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