4

次のようなサブプロジェクトを作成するAntタスクを使用する <antcall><ant>、次のいずれかのエラーが原因で繰り返し呼び出されるとビルドが失敗する可能性があります。

  • java.lang.OutOfMemoryError:PermGenスペース
  • java.lang.OutOfMemoryError:Javaヒープスペース

このエラーは、呼び出されているタスクの1つがまたはを使用して定義されている場合にのみ発生し、。などのAntにバンドルされているタスクを使用している<typedef>場合<taskdef>は表示されません<javadoc>

OutOfMemoryErrorJavaヒープの最大サイズを増やすことなく回避する方法はありますか?ヒープサイズを大きくするとしばらくは機能しますが、メモリを大量に消費するタスクを追加すると、問題が再発します。


次のタスク例と関連build.xmlファイルにより OutOfMemoryError、LinuxボックスでJavaヒープが10 MBに設定されます(テスト用)。Antタスクは、メモリを大量に消費するオブジェクト(この場合はClosure Template Soy Module用のGuiceインジェクター)を作成します。このオブジェクトは、を使用して繰り返し呼び出され<antcall>ます。

 

CreateGuiceInjectorTask.java

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.template.soy.SoyModule;

import org.apache.tools.ant.Task;

/** Custom Ant task that constructs a Guice injector. */
public final class CreateGuiceInjectorTask extends Task {
  private Injector injector;

  public CreateGuiceInjectorTask() {
    injector = Guice.createInjector(new SoyModule());
  }

  public void execute() {
    System.out.println("Constructed Guice injector...");
  }
}

 

build.xml

<?xml version="1.0" encoding="UTF-8"?>
<project name="out-of-memory-test" basedir=".">

  <property name="build.dir" location="${basedir}/build" />
  <property name="CreateGuiceInjectorTask.jar"
      location="${build.dir}/CreateGuiceInjectorTask.jar" />

  <taskdef name="create-injector"
      classname="CreateGuiceInjectorTask"
      classpath="${CreateGuiceInjectorTask.jar}" />

  <target name="call-create-injector">
    <create-injector />
  </target>

  <target name="test"
      description="Create multiple injectors until out of memory">
    <antcall target="call-create-injector" />
    <antcall target="call-create-injector" />
    <antcall target="call-create-injector" />
    <antcall target="call-create-injector" />
    <antcall target="call-create-injector" />
    <antcall target="call-create-injector" />
    <antcall target="call-create-injector" />
    <antcall target="call-create-injector" />
    <antcall target="call-create-injector" />
    <antcall target="call-create-injector" />
    <antcall target="call-create-injector" />
    <antcall target="call-create-injector" />
    <antcall target="call-create-injector" />
  </target>
</project>

 

テスト出力:

$ ant test

test:

call-create-injector:
[create-injector] Constructed Guice injector...

call-create-injector:
[create-injector] Constructed Guice injector...

...

call-create-injector:

BUILD FAILED
Could not create type create-injector due to java.lang.OutOfMemoryError: Java heap space
4

1 に答える 1

5

ここで説明されているKeith Gregory の方法を使用して、少なくとも投稿した単純なサンプル コードについては、メモリの問題を回避することができました。

大まかに言えば、Ant を介して taskdef を使用するたびantcallに異なるクラス ローダーが使用されるため、permgen をすぐに使い果たしてしまうという問題があります。これを確認するには、クラスローダーのハッシュ コードを出力するようにクラスを変更します。反復ごとに異なることがわかります。

回避策は、タスク定義をantlibとしてパッケージ化し、antlib 名前空間を使用してそれをロードすることです。結果として、Ant 独自のクラスローダーが使用されます。これが機能するには、Ant クラスパスにクラスを配置する必要があります。

これをテストするために、テスト クラスをパッケージ名前空間 (memtest と呼ばれる) に配置し、コンパイルしてから、次のようなパッケージ ディレクトリに antlib.xml を追加しました。

<antlib>
  <taskdef name="create-injector" classname="memtest.CreateGuiceInjectorTask" />
</antlib>

buildfile プロジェクトの宣言が次のように変更されました。

<project name="out-of-memory-test" basedir="." default="test" xmlns:mt="antlib:memtest">

そして目標は

<target name="call-create-injector">
  <mt:create-injector />
</target>

テストするために、必要なものをすべて Ant クラスパスに配置しました。多くの antcall が正常に実行され、クラスローダー ハッシュ コードのデバッグ出力により、予期された単一のインスタンスが使用されたことが確認されました。

antlib 名前空間の Ant ドキュメントには、「リソースがデフォルトのクラスパスにあるという要件は、Ant の将来のバージョンでは削除される可能性がある」というメモがあります。実装方法によっては、回避策が将来的に機能しなくなる可能性があります。

于 2012-05-19T12:34:37.733 に答える