Ant <javac> tasks throw a StackOverflowException
I am trying to compile over 100 Java classes from different packages from pure (without incremental compilation) using the following ant tasks:
<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> However, the first time I run the compilation task, I always get a StackOverflowException. If I run the task again, the compiler will perform an incremental build and everything will be fine. This is undesirable since we use CruiseControl to perform automatic daily builds, and this causes false build failures.
As a quick and dirty solution, I created two separate tasks, each of which was part of the project. I really don’t think that this decision will be made as more classes are added in the future, and I don’t want to add new compilation tasks every time we click the “compilation limit”.
Good to know; What can or does a StackOverflowError raise while compiling Java code?
It is likely that evaluating a long expression in your java file consumes a lot of memory, and since this is done in conjunction with compiling other classes, the VM simply ends up in stack space. Your generated class may be pushing the legal limits of its content. See Chapter 4.10 Limitations of the Java Virtual Machine in the Java Virtual Machine Specification, second edition .
Fix 1: reorganize the class
Since your class is generated, this may not be an option. However, it is worth taking a look at the options offered by your class generation tool to see if it can create something less unpleasant.
Fix 2: increase stack size
I think Kieron has one solution when it mentions the -Xss argument. javac accepts several non-standard arguments that will vary between versions and compiler providers.
My compiler:
$ javac -version javac 1.6.0_05 To list all the options for this, I would use the following commands:
javac -help javac -X javac -JX I think the stack limit for javac is 512KB by default. You can increase the stack size for this compiler to 10 MB with this command:
javac -J-Xss10M Foo.java You might be able to pass this in an Ant file with the compilerarg element nested in your javac task.
<javac srcdir="gen" destdir="gen-bin" debug="on" fork="true"> <compilerarg value="-J-Xss10M" /> </javac> Try adding some changes to these attributes in the Ant javac line:
memoryinitialsize="256M" memorymaximumsize="1024M" You can also try fork="true" , not sure if this allows you to set values for the stack and heap (aka -Xm1024), but it can help (if it works from the command line, but not in Ant).
[Edit]: Added link - the javac page would show that the above options require that you also set fork="true" .
<javac srcdir="gen" destdir="gen-bin" debug="on" fork="true"> <compilerarg value="-J-Xss10M" /> </javac> from the comment above is incorrect. You need a space between -J and -X, for example:
<javac srcdir="gen" destdir="gen-bin" debug="on" fork="true"> <compilerarg value="-J -Xss10M" /> </javac> to avoid the following error:
[javac] [javac] The ' characters around the executable and arguments are [javac] not part of the command. [javac] Files to be compiled: ... [javac] javac: invalid flag: -J-Xss1m [javac] Usage: javac
It's pretty weird, 100 classes really aren't many. What does the compiler do when the stack overflows? Is there a useful stack trace? What happens if you run javac directly on the command line instead of thorugh ant?
One possible workaround is to simply increase the stack size using the -Xss argument to the JVM; either for a JVM running ant , or by setting fork="true" and a <compilerarg> in the <javac> task. Actually now, when I think about it, the problem disappears just by inserting fork="true" ?
Here is what I found. After posting my question, I continued and changed the compilation task with the attributes fork="true" , memoryinitialsize="256m" and memorymaximumsize="1024m" (found today that it was suggested by Kieron and jmanning2k, thanks for your time). However, this did not solve the problem.
I decided to start removing classes from the source tree to find out if the problem could determine the problem. It turns out that we had a Web Service client class for Axis 1.4 , which was automatically generated from the WSDL file. Now this class is a monster (as in Frankenstein), it has 167 field members (all of them are of type String), 167 getter / setter pairs (1 for each field), a constructor that receives all 167 fields as parameters, equals, which compares all 167 fields in a weird way. For each field, the comparison is performed as follows:
(this.A == null && other.getA() == null) || (this.A != null && this.A.equals(other.getA())) The result of this comparison is "anded" (&) with the result of comparing the next field, etc. The class continues with the hashCode method, which also uses all fields, some custom XML serialization methods, and a method that returns an axis-specific metadata object that describes the class, and which also uses all field members.
This class never changes, so I just put the compiled version in the class path of the application, and the project was compiled without problems.
Now I know that deleting this single source file solved the problem. However, I absolutely don't know why this particular class caused the problem. It will be good to know; What can raise or raise a StackOverflowError while compiling Java code? I think I will post this question.
For those who wish:
- Windows XP SP2
- SUN JDK 1.4.2_17
- Ant 1.7.0