Using Ant tasks that create subprojects such as <antcall>
and <ant>
may cause build failures when invoked repeatedly due to one of the following errors:
- java.lang.OutOfMemoryError: PermGen space
- java.lang.OutOfMemoryError: Java heap space
The error only occurs when one of the tasks being called is defined using <typedef>
or <taskdef>
and does not appear when using tasks bundled with Ant such as <javadoc>
.
Is there a way to avoid the OutOfMemoryError
without increasing the
maximum Java heap size? Although increasing the heap size works for a
while, the problem still resurfaces if more memory intensive tasks are added.
The following example task and associated build.xml
file cause an
OutOfMemoryError
on my Linux box with the Java heap set to 10 MB (for
testing). The Ant task constructs a memory-hungry object (in this case a Guice
injector for a Closure Template Soy Module), which is then repeatedly called
using <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>
Test Output:
$ 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
Using Keith Gregory's method described here I was able to work around the memory problem, at least for the simple example code you posted.
The problem, in outline, is that each time you use a taskdef via an
antcall
Ant uses a different class loader, hence you eat up your permgen in no time. To confirm this you could modify your class to print the classloader hash code - you'll see its different on each iteration.The work-around is to package your taskdef as an antlib and use the antlib namespace to load it. The upshot is that Ant's own classloader is used. For this to work you must place the class on the Ant classpath.
To test this I put your test class in a package namespace (called memtest), compiled, then added an antlib.xml in the package directory that looks like this:
The buildfile project declaration was changed to
and the target to
To test I put everything needed on the Ant classpath. It ran many antcalls successfully, and the debug print of the classloader hash code confirmed the expected single instance was used.
There's a note in the Ant documentation for the antlib namespace that says "the requirement that the resource is in the default classpath may be removed in future versions of Ant." Depending on how that is implemented the work-around may break in future.