Ant taskdef - 耗尽PermGen空间

Chr*_*ert 4 java ant typedef out-of-memory taskdef

使用创建子项目,如Ant任务 <antcall>,并<ant>可能导致测试失败重复调用因下列错误之一时:

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

只有在使用<typedef><taskdef>使用捆绑的任务时才会出现调用的任务之一时出现错误,例如<javadoc>.

有没有办法避免OutOfMemoryError不增加最大Java堆大小?虽然增加堆大小可以工作一段时间,但如果添加更多内存密集型任务,问题仍然会重新出现.


以下示例任务和关联build.xml文件导致 OutOfMemoryError我的Linux机箱上的Java堆设置为10 MB(用于测试).Ant任务构造了一个需要内存的对象(在这种情况下是一个Closure模板大豆模块的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...");
  }
}
Run Code Online (Sandbox Code Playgroud)

 

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>
Run Code Online (Sandbox Code Playgroud)

 

测试输出:

$ 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
Run Code Online (Sandbox Code Playgroud)

mar*_*ton 5

使用Keith Gregory 在此描述方法,我能够解决内存问题,至少对于您发布的简单示例代码.

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

解决方法是将taskdef打包为antlib并使用antlib命名空间加载它.结果是使用了Ant自己的类加载器.为此,您必须将类放在Ant类路径上.

为了测试这个,我将测试类放在一个包名称空间(称为memtest)中,编译,然后在包目录中添加一个antlib.xml,如下所示:

<antlib>
  <taskdef name="create-injector" classname="memtest.CreateGuiceInjectorTask" />
</antlib>
Run Code Online (Sandbox Code Playgroud)

构建文件项目声明已更改为

<project name="out-of-memory-test" basedir="." default="test" xmlns:mt="antlib:memtest">
Run Code Online (Sandbox Code Playgroud)

和目标

<target name="call-create-injector">
  <mt:create-injector />
</target>
Run Code Online (Sandbox Code Playgroud)

为了测试我把所有需要放在Ant类路径上.它成功运行了许多antcall,并且类加载器哈希码的调试打印确认了使用了预期的单个实例.

Ant文档中有一个关于antlib命名空间的注释,其中说"在未来的Ant版本中可能会删除资源在默认类路径中的要求".根据实施方式的不同,未来的解决方案可能会中断.