8

私は約 800 個のソース ファイル (javacc/JTB によって生成されたもの) を含む Java Maven プロジェクトを持っていますが、これは javac でコンパイルするのに 25 分かかります。

Eclipse コンパイラーを使用するように pom.xml を変更したところ、コンパイルに約 30 秒かかりました。

javac (1.5) の実行速度が非常に遅い理由について何か提案はありますか? (Maven のプラグインには少しバグがあるように見えるので、Eclipse コンパイラーに永久に切り替えたくありません。)

問題を簡単に再現できるテストケースがあります。次のコードは、既定のパッケージに多数のソース ファイルを生成します。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();
    }
}
4

8 に答える 8

7

Sun は、これが新しいバグであることを電子メールで確認しました (バグ データベースでは6827648 )。

于 2009-04-08T12:03:57.063 に答える
6

アップデート14、ビルド04を含むJDK 1.6でも同じ動作が得られますが、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.
 */

日食のコンパイラがそのチェックを実行しないか、同じ方法で実行しない可能性があります。

于 2009-04-08T03:53:02.843 に答える
2

生成されたソースを使用しているという事実、速度の大幅なStackOverflowError違い、および1 つ (または複数) のファイルにjavacパーサーが同意しない構成が含まれている可能性があります。

コードのサブセットのみをコンパイルして、1 つのクラス/パッケージが特にプロセスを遅くするかどうかを確認してください (おそらく生成されたものの 1 つ)。

于 2009-03-27T08:44:27.553 に答える
0

おそらく、Eclipseビルドは変更されたソースをコンパイルしているだけです。クリーンアップ後にEclipseでコンパイルするとどうなりますか?

于 2009-03-26T16:42:02.377 に答える
0

maven がコンパイラを呼び出す方法はわかりませんが、あなたが言及したパフォーマンスの数値は、別の回答で既に示唆されているように、javac が独自のプロセス/VM で実行されていることを示唆しています。コンパイルするファイルごとに新しいプロセス/VM を開始するのは非常にコストがかかるため、すでにある VM を使用するようにコンパイラを構成する必要があります。ANTがそれを提供していることは知っていますが、私自身はmavenを使用していません。人気があるという事実を考えると、それほど重要な機能が欠けているとは思えません。

于 2009-04-03T20:05:59.273 に答える
0

次のようなことが起こっていると思います: Maven は javac をフォークし、ライフサイクルの個別のステップの JVM プロセス: Maven Build Life-cycle

Eclipse は通常、バックグラウンドで (保存時に) コンパイルを実行するため、そのステップがコンパイル フェーズに追加されます。実質的な依存関係がある場合、ここでスループットが失われます。

さらに、(mvn の構成に応じて) 各テスト メソッドは独自の JVM を取得します。テストの通過はパッケージ フェーズの前提条件であるため、JUnit テストの実行に時間がかかる可能性があります (特にテストの実行が遅い場合)。ソース ツリーに大量のテスト コードがある場合にのみ、これが原因となる可能性があります。

ほとんどの場合、あなたのクラスはかなりの量のファイル I/O を行うため、これはチャンスの領域です。ファイル検出イベントごとにループが 1000 回実行されているように見えます。つまり、ループの本体で 800*1000 = 800,000 回の PrintStream が作成されます。

于 2009-04-06T18:08:49.670 に答える