Bug 309336 - Build problems / OOM in Eclipse
Summary: Build problems / OOM in Eclipse
Status: RESOLVED FIXED
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: 1.6.9   Edit
Hardware: PC Windows Server 2003
: P2 major (vote)
Target Milestone: 1.6.9M2   Edit
Assignee: aspectj inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-04-15 11:47 EDT by Martin Schaffoener CLA
Modified: 2010-05-13 13:48 EDT (History)
1 user (show)

See Also:


Attachments
Failing project (12.44 KB, application/unknown)
2010-04-15 11:47 EDT, Martin Schaffoener CLA
no flags Details
Project compiling fine (12.84 KB, application/unknown)
2010-04-15 11:47 EDT, Martin Schaffoener CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Martin Schaffoener CLA 2010-04-15 11:47:09 EDT
Created attachment 164985 [details]
Failing project

A bug report as suggested by Andy Clement on the ML, I'm pasting from the original email.

I'm currently investigating the feasibility to apply AspectJ to a larger commercial code base. While test-driving Eclipse and Ant tasks I found some odd behavior. Maybe it's me, maybe it's a bug.

The first thing I could drill down on is a build in Eclipse hanging the entire IDE and eating ridiculous amounts of memory. The code with which I can reproduce this problem is like this:

We first have an interface which defines one method which throws exceptions specified by generic type arguments, like this:

interface Op14<Ret, E1 extends Throwable, E2 extends Throwable, ..., E14 extends Throwable> {
Public Ret execute(String aArg) throws RemoteException, E1, E2, ..., E14;
}

Then we specialize on the interface in a telescoping-style pattern to reduce the number of exceptions that need to be specified:

// Extend up to Op14
interface Op1<Ret, E1 extends Throwable> extends Op2<Ret, E1, E1> {}
interface Op0<Ret> extends Op1<Ret, RuntimeException> {}

Then we have a separate class were the Op0 interface is implemented in an anonymous inner class:

Public class UseOperator {
Void method() throws ...
{
Op0<String> f = new Op0<String>(){
String execute(String aArg) throws RemoteException
{
System.out.println("Doh!");
Return aArg;
}
f.execute("");
}
}

And finally we have a simple aspect:

Public aspect NoSystemStreams {
Declare warning : get(java.io.PrintStream System.out) : "No system.out";
Declare warning : get(java.io.PrintStream System.err) : "No system.err";
}

This still compiles fine. However, if I change the warning declaration to

Declare warning : within(com.msr..*) && get(java.io.PrintStream System.out) : "No system.out";
Declare warning : within(com.msr..*) && get(java.io.PrintStream System.err) : "No system.err";

(Note the difference to the original email: I changed both warning declaration to be restricted to classes in the com.msr package; original warnings were unrestricted)

Compilation never finishes and starts eating what seems like arbitrary amounts of heap (max Eclipse heap here was 2.5GB, and it did not suffice!). It also seems like when reducing the inheritance depth to Op13, the problem also disappears.

This is a show-stopper as AspectJ cannot handle the grown code base. If you like, I'll file a bug report with archives of working and failing Eclipse projects.

All of this has been tried with Eclipse 3.5.2 and both AJDT 2.0.2 and 2.0.3.e35x-20100410-1900; the problem occurs independently of the weaving service being enabled or not.
Comment 1 Martin Schaffoener CLA 2010-04-15 11:47:46 EDT
Created attachment 164986 [details]
Project compiling fine
Comment 2 Andrew Clement CLA 2010-04-15 13:58:04 EDT
That is an interesting hierarchy there that uses generics, I've never seen anything like it.  I see a couple of issues when I attempt to recreate, but finally have a scenario that will reliably exhaust memory.

Reproduceable on the command line if the files are compiled in a particular order:

ajc -1.5 NoSystemStreams.aj UseOperators.java Operators.java
[error] AspectJ DEVELOPMENT ran out of memory during compilation:

Please increase the memory available to ajc by editing the ajc script
found in your AspectJ installation directory. The -Xmx parameter value
should be increased from 64M (default) to 128M or even 256M.

See the AspectJ FAQ available from the documentation link
on the AspectJ home page at http://www.eclipse.org/aspectj

which means that it is pipeline related, because UseOperators will be through the pipeline before Operators.  Compiling the other way around

>ajc -1.5 NoSystemStreams.aj Operators.java UseOperators.java
UseOperators.java:11 [warning] No system.out
System.out.println("Doh!");
^^^^^^^^^^^^^^^^^^^^^^^^^^^
        field-get(java.io.PrintStream java.lang.System.out)
        see also: N:\309336\working\ajdt-oom\src\com\msr\NoSystemStreams.aj:3::0

1 warning

works as expected.

That does mean you could probably progress if you turn off pipelining.  That is done by opening the project properties, navigate to AspectJ Compiler, then on the right enable project specific settings, go down to the non-standard compiler options box and enter '-Xset:pipelineCompilation=false'
Comment 3 Andrew Clement CLA 2010-04-15 14:25:16 EDT
all the time/memory is consumed in the PartialOrder.sort(List) method - which never exits this loop:
 
        for (Iterator i = objects.iterator(); i.hasNext(); ) {
            addNewPartialComparable(sortList, (PartialComparable)i.next());
        }

The issue is with sorting the 'addedSuperInitializers', invoked from BcelClassWeaver.weave():

addedSuperInitializersAsList = PartialOrder.sort(addedSuperInitializersAsList);
Comment 4 Martin Schaffoener CLA 2010-04-16 02:41:00 EDT
Turning of pipeline compilation fixes the OOM situation for me. Both my test project and the real-world code are compiling fine under Eclipse.
Comment 5 Andrew Clement CLA 2010-04-16 17:15:49 EDT
ok, the problem here is the sort algorithm really stresses the method 'ReferenceType.isAssignableFrom()'.  Due to the deep hierarchy of parameterized types in the testcode, isAssignableFrom() is wasting all kinds of time and making unnecessary recursive calls.  I have tagged the method in the past that it needs a rewrite but we were limping along ok.  This bug suggests I ought to get on with things.

I extracted the sort and the deep hierarchy into an individual testcase and ran some benchmarks on it.

executing the 'sort' 10 times when the 14 levels of interface are to be sorted results in 2716810 calls to isAssignableFrom() for various combinations of the interface types  (and their superinterfaces).  It is basically comparing all of them with each other to come up with an ordering.  These 2716810 calls take about 1100ms on my laptop after its warmed up on a few runs.

I've now reworked the algorithm.  We are now doing 105020 calls for the same thing and it is taking 23ms.

This will actually speed up AspectJ in every common scenario: compile time weaving, binary weaving, load time weaving.  Not sure what the impact will be, but it will be quicker.

it will be in an AJDT build in a few days, i want to also include the fix for your other issue too before building a new AJDT.