Почему javac 1.5 работает так медленно по сравнению с компилятором Eclipse? - PullRequest
8 голосов
/ 26 марта 2009

У меня есть проект Java Maven с около 800 исходными файлами (некоторые сгенерированы javacc / JTB), который компилируется с javac за 25 минут.

Когда я изменил свой pom.xml на использование компилятора Eclipse, компиляция занимает около 30 секунд.

Есть предложения, почему javac (1.5) работает так медленно? (Я не хочу постоянно переключаться на компилятор Eclipse, так как плагин для Maven кажется более чем глючным.)

У меня есть тестовый пример, который легко воспроизводит проблему. Следующий код генерирует несколько исходных файлов в пакете по умолчанию. Если вы попытаетесь скомпилировать ImplementingClass.java с помощью javac, он будет казаться приостановленным на долгое время.

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 ]

7 голосов
/ 08 апреля 2009

Sun подтвердил мне по электронной почте, что это новая ошибка ( 6827648 в их базе данных ошибок).

6 голосов
/ 08 апреля 2009

Вы получаете то же поведение с JDK 1.6, включая обновление 14, сборка 04, использование G1 не меняет поведение (хотя G1, кажется, работает очень хорошо).

Мониторинг javac с помощью jvisualvm, повторные дампы потоков показывают, что основной поток тратит много времени на

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 либо не выполняет эту проверку, либо не выполняет ее аналогичным образом.

5 голосов
/ 03 апреля 2009

Может случиться так, что компилятор javac работает близко к пределу кучи (64 МБ или около того). В этом случае он проводит большую часть времени в сборщике мусора. Дайте компилятору хороший кусок памяти, скажем 256M или 512M, и посмотрите, работает ли он быстрее.

2 голосов
/ 27 марта 2009

Тот факт, что вы используете сгенерированный источник, огромная разница в скорости и StackOverflowError могут указывать на то, что один (или более) из ваших файлов имеют некоторые конструкции, которые парсер javac не делает. не согласен с.

Не могли бы вы попытаться скомпилировать только подмножества вашего кода и посмотреть, замедляет ли какой-либо один класс / пакет процесс особенно (вероятно, один из сгенерированных).

1 голос
/ 26 марта 2009

Для компилятора Sun вы запускаете целый процесс JVM для каждого файла, который вы хотите скомпилировать. Для компилятора Eclipse это просто подключение к процессу демона. Я предлагаю установить для fork значение false, хотя это может быть не так быстро.

0 голосов
/ 06 апреля 2009

Я думаю, что происходит что-то вроде следующего: Maven форкс javac, JVM обрабатывает отдельные этапы своего жизненного цикла: Жизненный цикл сборки Maven

Eclipse обычно запускает компиляцию в фоновом режиме (при сохранении), поэтому этот шаг будет добавлен на этапе компиляции. Если есть существенные зависимости, то здесь вы теряете пропускную способность.

Кроме того (в зависимости от конфигурации mvn) каждый метод тестирования получает свою собственную JVM. Поскольку прохождение теста является предварительным требованием к фазе пакета, возможно, вы теряете время на выполнение тестов JUnit (особенно, если они выполняются медленно). Это только вероятный виновник, если у вас есть много тестового кода в вашем исходном дереве.

Скорее всего, ваш класс выполняет значительные объемы файлового ввода-вывода, так что это область возможностей. Похоже, ваш цикл выполняется 1000 раз для каждого события обнаружения файла, что означает 800 * 1000 = 800 000 созданий PrintStream в теле цикла.

0 голосов
/ 04 апреля 2009

Я не знаю, как maven вызывает компилятор, но упомянутые вами номера производительности предполагают, что javac выполняется в своем собственном процессе / VM, как уже предлагалось в другом ответе. Поскольку запуск нового процесса / виртуальной машины для каждого файла, который вы компилируете, является очень дорогостоящим, вам необходимо убедиться, что компилятор настроен на использование виртуальной машины, которая у вас уже есть. Я знаю, что ANT предлагает это, но я сам не использовал maven. Учитывая тот факт, что он популярен, я сомневаюсь, что ему не хватает такой важной функции.

0 голосов
/ 26 марта 2009

Возможно, сборка Eclipse компилирует только модифицированный исходный код. Что произойдет, если вы скомпилируете его в Eclipse после очистки?

...