Ant の <javac> タスクが StackOverflowException をスローする
質問
次のANTタスクを使用して、クリーンディレクトリからの異なるパッケージから100を超えるJavaクラスをコンパイルしようとしています(インクリメンタルコンパイルなし)。
<target name="-main-src-depend">
<depend srcdir="${src.dir}"
destdir="${bin.dir}"
cache="${cache.dir}"
closure="true"/>
</target>
<target name="compile" depends="-main-src-depend"
description="Compiles the project.">
<echo>Compiling</echo>
<javac target="${javac.target}"
source="${javac.source}"
debug="${javac.debug}"
srcdir="${src.dir}"
destdir="${bin.dir}">
<classpath>
<path refid="runtime.classpath"/>
<path refid="compile.classpath"/>
</classpath>
</javac>
</target>
ただし、初めてコンパイル タスクを実行すると、常に StackOverflowException が発生します。タスクを再度実行すると、コンパイラーは増分ビルドを実行し、すべてが正常に動作します。を使用しているため、これは望ましくありません。 クルーズコントロール 自動毎日ビルドを実行するため、誤ったビルド失敗が発生します。
簡単な解決策として、2 つの別々のタスクを作成し、それぞれでプロジェクトの一部をコンパイルしました。将来さらに多くのクラスが追加されるときに、この解決策が有効であるとは本当に思いません。また、「コンパイル制限」に達するたびに新しいコンパイル タスクを追加することも望んでいません。
解決
知っておくと良いでしょう。Javaコードの編集中にStackoverFlowerrorを引き起こすか、原因とするものは何ですか?
おそらく、Java ファイル内の長い式の評価は大量のメモリを消費し、これは他のクラスのコンパイルと併せて実行されるため、VM のスタック領域が不足してしまう可能性があります。生成されたクラスは、そのコンテンツの法的制限を超えている可能性があります。章を参照 4.10 Java仮想マシンの制限事項 で Java 仮想マシン仕様、第 2 版.
修正 1:クラスをリファクタリングする
クラスが生成されているため、これはオプションではない可能性があります。それでも、クラス生成ツールが提供するオプションを検討して、それほど面倒ではないものを生成できるかどうかを確認する価値はあります。
修正 2:スタックサイズを増やす
私は思う キーロン -Xss 引数について言及したとき、解決策が 1 つあります。 ジャバック は、バージョンやコンパイラ ベンダーによって異なる多数の非標準引数を受け取ります。
私のコンパイラ:
$ javac -version
javac 1.6.0_05
すべてのオプションをリストするには、次のコマンドを使用します。
javac -help
javac -X
javac -J-X
私 考える javac のスタック制限はデフォルトで 512Kb です。次のコマンドを使用して、このコンパイラのスタック サイズを 10Mb に増やすことができます。
javac -J-Xss10M Foo.java
これを Ant ファイルで渡すこともできるかもしれません。 コンパイラーグ ネストされた要素 ジャバック タスク。
<javac srcdir="gen" destdir="gen-bin" debug="on" fork="true">
<compilerarg value="-J-Xss10M" />
</javac>
他のヒント
<javac srcdir="gen" destdir="gen-bin" debug="on" fork="true">
<compilerarg value="-J-Xss10M" />
</javac>
から 上のコメント 間違っています。次のように、-J と -X の間にスペースが必要です。
<javac srcdir="gen" destdir="gen-bin" debug="on" fork="true">
<compilerarg value="-J -Xss10M" />
</javac>
次のエラーを回避するには:
[javac]
[javac] The ' characters around the executable and arguments are
[javac] not part of the command.
[javac] Files to be compiled:
...[javac] javac:無効なフラグ:-j-xss1m [javac]使用法:ジャバック
これらの属性のバリエーションをいくつか追加してみてください。 蟻 javac
タスク ライン:
memoryinitialsize="256M" memorymaximumsize="1024M"
試してみることもできます fork="true"
, これによりスタックとヒープ (別名 -Xm1024) の値を設定できるかどうかはわかりませんが、役立つかもしれません (Ant ではなくコマンドラインから機能する場合)。
[編集]:リンクを追加しました -- javac
タスク このページでは、上記のパラメータを設定する必要があることを示唆しているようです。 fork="true"
.
コマンド ラインから javac コマンドを実行すると、この問題が発生しますか?試してみるとよいかもしれません フォーク 属性。
これはかなり奇妙です。100 クラスは実際にはそれほど多くありません。スタックがオーバーフローした場合、コンパイラは何をしているのでしょうか?有用なスタック トレースは生成されていますか?走ったらどうなるか javac
ant 経由ではなくコマンドラインで直接?
考えられる回避策の 1 つは、次のコマンドを使用してスタックのサイズを単純に増やすことです。 -Xss
JVM への引数。実行中の JVM に ant
または設定により fork="true"
そして <compilerarg>
で <javac>
タスク。実際、今考えてみると、を入れるだけで問題は解決しますか? fork="true"
?
これが私が見つけたものです。質問を投稿した後、属性を使用してコンパイルタスクを変更しました。 fork="true"
, memoryinitialsize="256m"
そして memorymaximumsize="1024m"
(今日、これが Kieron と jmanning2k によって提案されたものであることがわかりました。お時間をいただきありがとうございました)。それでも問題は解決しませんでした。
私は、問題を正確に特定できるかどうかを確認するために、ソース ツリーからクラスを削除し始めることにしました。Web サービス クライアント クラスがあることがわかりました。 軸1.4 これは WSDL ファイルから自動生成されました。さて、このクラスは (フランケンシュタインのような) モンスターであり、167 個のフィールド メンバー (すべて文字列型)、167 個のゲッター/セッター ペア (フィールドごとに 1 つ)、167 個のフィールドすべてをパラメーターとして受け取るコンストラクター、 167 個のフィールドすべてを奇妙な方法で比較する平等メソッド。各フィールドの比較は次のようになります。
(this.A == null && other.getA() == null) || (this.A != null && this.A.equals(other.getA()))
この比較の結果は、次のフィールドの比較の結果と「論理積」(&&) されます。このクラスは、すべてのフィールドを使用する hashCode メソッド、いくつかのカスタム XML シリアル化メソッド、およびクラスを記述しすべてのフィールド メンバーを使用する Axis 固有のメタデータ オブジェクトを返すメソッドを続けます。
このクラスは決して変更されないため、コンパイルされたバージョンをアプリケーションのクラスパスに置くだけで、プロジェクトは問題なくコンパイルされました。
この 1 つのソース ファイルを削除することで問題が解決したことがわかりました。ただし、この特定のクラスが問題を引き起こした理由はまったくわかりません。知っておくと良いでしょう。Java コードのコンパイル中に StackOverflowError が発生する原因は何ですか?その質問を投稿しようと思います。
興味のある方へ:
- Windows XP SP2
- SUN の JDK 1.4.2_17
- アント 1.7.0