4

使用创建子项目的 Ant 任务,例如 <antcall>并且<ant>可能由于以下错误之一而在重复调用时导致构建失败:

  • java.lang.OutOfMemoryError: PermGen 空间
  • java.lang.OutOfMemoryError:Java 堆空间

仅当正在调用的任务之一使用<typedef>or定义时才会发生错误<taskdef>,并且在使用与 Ant 捆绑在一起的任务(例如<javadoc>.

有没有办法在OutOfMemoryError不增加最大 Java 堆大小的情况下避免这种情况?虽然增加堆大小暂时有效,但如果添加更多内存密集型任务,问题仍然会重新出现。


以下示例任务和关联build.xml文件 OutOfMemoryError在我的 Linux 机器上导致 Java 堆设置为 10 MB(用于测试)。Ant 任务构造了一个需要大量内存的对象(在本例中为闭包模板大豆模块的 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...");
  }
}

 

构建.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 的方法,我能够解决内存问题,至少对于您发布的简单示例代码而言。

概括地说,问题是每次通过antcallAnt 使用 taskdef 时都会使用不同的类加载器,因此您很快就会吃光您的 permgen。要确认这一点,您可以修改您的类以打印类加载器哈希码 - 您将在每次迭代中看到它的不同。

解决方法是将您的 taskdef 打包为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,并且类加载器哈希码的调试打印确认使用了预期的单个实例。

Ant 文档中有一条关于 antlib 命名空间的注释说“在未来的 Ant 版本中,可能会删除资源位于默认类路径中的要求”。根据如何实施,解决方法可能会在未来中断。

于 2012-05-19T12:34:37.733 回答