Eclipse コンパイラと比べて、javac 1.5 の実行速度が非常に遅いのはなぜですか?
質問
約 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();
}
}
解決
あなたは(G1は本当によく動作しているように見えますが)、動作を変更しませんG1を使用して、04を構築、更新14を含むJDK 1.6と同じ動作を取得ます。
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
Iコードが他のすべてのメソッドと、各メソッドを比較com.sun.tools.javac.comp.Check.checkCompatibleConcretes
介しかき回すさ疑い
そのメソッドのjavadocのます:
/** Check that a class does not inherit two concrete methods
* with the same signature.
*/
これは、そのチェックを実行しない日食のコンパイラのいずれであってもよく、または同じ方法でそれを実行しません。
他のヒント
Sunは 6827648(これは新しいバグがあることを電子メールで私に確認していますの彼らのバグデータベースで)ます。
これはjavacコンパイラがヒープ限界(64メガバイト程度)に近い動作することであってもよいです。その場合には、ガベージコレクタにほとんどの時間を費やしています。コンパイラにメモリのかなりの部分を与え、256Mや512Mを言うと、それはより速く走るかどうかを確認します。
は、生成されたソースを使用しているという事実を、の大規模なの速度差と、あなたのファイルがStackOverflowError
パーサが一致しないと、いくつかの構造を持っているのjavac
はその1を示唆(またはそれ以上)かもしれませんでます。
あなたはあなたのコードのサブセットのみをコンパイルし、いずれかのクラス/パッケージは、特に(おそらく、生成されたものの一つ)のプロセスを遅くするかどうかを確認しようとすることができます。
Sunのコンパイラのためにあなたがコンパイルしたい各ファイルの全体のJVMプロセスを起動しています。 Eclipseのコンパイラのためにそれだけでデーモンプロセスに接続しています。それはまだ非常に高速ではないかもしれないが、私は、falseに設定フォークをお勧めします。
おそらく、Eclipseのビルドにのみ変更されたソースをコンパイルしています。あなたはクリーン後に日食でコンパイルした場合はどうなりますか?
私は、Mavenのは、コンパイラを呼び出しますが、あなたが言及パフォーマンスの数値は、既に別の答えで提案されているようにjavacのは、それ自身のプロセス/ VMで実行されることを示唆しているのか分かりません。新しいプロセスを開始したよう/コンパイルすべてのファイルのためのVMは、あなたが持っているかもしれないalreay VMを使用するようにコンパイラを設定するために確保する必要があり、非常に高価です。私はANTがあることを提供しています知っているが、私は自分自身をMavenを使用していません。それが人気であるという事実を考えると、私はそれが、このような重要な機能が欠けていることを疑うます。
次のようなことが起こっていると思います。Maven は javac をフォークし、JVM はそのライフサイクルの個別のステップを処理します。 Maven ビルドのライフサイクル
Eclipse は通常、コンパイルをバックグラウンド (保存時) で実行するため、そのステップはコンパイル フェーズに追加されます。重大な依存関係がある場合、ここでスループットが低下します。
さらに、(mvn 構成に応じて) 各テスト メソッドは独自の JVM を取得します。テスト通過はパッケージ フェーズの前提条件であるため、JUnit テストの実行に時間がかかる可能性があります (特に実行速度の遅いテストの場合)。これが原因となる可能性があるのは、ソース ツリーに多数のテスト コードがある場合のみです。
最も可能性が高いのは、クラスが大量のファイル I/O を実行するため、これがチャンスの領域であるということです。ループはファイル検出イベントごとに 1000 回実行されているようです。つまり、ループ本体で 800*1000 = 800,000 回の PrintStream が作成されることになります。