Community
Participate
Working Groups
Build ID: M20060921-0945 Steps To Reproduce: Run the following sample program: public aspect StrongRef { // this advice leaks o! after() returning ( Object o ) : call( Object.new( .. ) ) {} public static void main( String[] args ) { Map< Object, Object > mWeak = new WeakHashMap< Object, Object >(); mWeak.put( new Object(), new Object() ); flushMemory(); System.out.println( "object was " + ( 0 != mWeak.size() ? "not " : "" ) + "reclaimed" ); } public static void flushMemory() { Vector< byte[] > v = new Vector< byte[] >(); int count = 0; int size = 1 << 20; while ( size > 1 ) try { for (; true ; count++) v.addElement( new byte[ size ] ); } catch( OutOfMemoryError bounded ){ size >>= 1; } v = null; //release for GC v = new Vector< byte[] >(); } } More information: run as is, will output "object was not reclaimed". comment out the "returning ( Object o )" in the after advice, and the output is "object was reclaimed".
the following workaround is only acceptable if you wish to advise the constructor calls of a class that is part of the same project: // this advice doesn't leak mo after( MyObject mo ) : execution( MyObject.new( .. ) ) && this ( mo ) {}
I always find testing for memory leaks is hard - especially when references are involved since they never seem to behave as described... Here is a variation of your program which should have the same problem: public aspect StrongRef { // this advice leaks o! after() returning (Object o) : call( Object.new( .. ) ) {} public static void main( String[] args ) { Map< Object, Object > mWeak = new WeakHashMap< Object, Object >(); for (int i=0;i<5;i++) { mWeak.put( new Object(), new Object() ); } flushMemory(); System.out.println("mWeak size is "+mWeak.size()); System.out.println( "object was " + ( 0 != mWeak.size() ? "not " : "" ) + "reclaimed" ); } public static void flushMemory() { Vector< byte[] > v = new Vector< byte[] >(); int count = 0; int size = 1 << 20; while ( size > 1 ) try { for (; true ; count++) v.addElement( new byte[ size ] ); } catch( OutOfMemoryError bounded ){ size >>= 1; } v = null; //release for GC v = new Vector< byte[] >(); } } but it doesn't it runs fine: pr200399\>ajc -1.5 -showWeaveInfo StrongRef.java Join point 'constructor-call(void java.lang.Object.<init>())' in Type 'StrongRef' (StrongRef.java:10) advised by afterReturning advice from 'StrongRef' (StrongRef.java:5) Join point 'constructor-call(void java.lang.Object.<init>())' in Type 'StrongRef' (StrongRef.java:10) advised by afterReturning advice from 'StrongRef' (StrongRef.java:5) pr200399\>java StrongRef mWeak size is 0 object was reclaimed Looking at the generated code, I can't see where the leak would be anyway. I might believe there was a leak if you were using a different instantiation model than singleton (perthis or something) - but from the program I just ran and the generated code, I don't think there is anything to fix and the collection of references is up to the VM. (i am on a 1.5.0 on the Mac) I was then trying to take a heap dump and use jhat to investigate where the references were, to ensure there were no strong ones - but jhat on the mac is crashing with NPEs, stupid thing.
I can see the difference now between the use of after() and after() returning. When using after() returning, the result of the ctor call to Object.new() is stored in a local variable in the method so that it can be passed to the advice. The act of anchoring it in the local variable means it is not available for garbage collection. Your program does not let the new Object instance go out of scope before attempting to fill up memory, and so they don't disappear. If I change your method: public static void main( String[] args ) { Map< Object, Object > mWeak = new WeakHashMap< Object, Object>(); mWeak.put( new Object(), new Object() ); flushMemory(); System.out.println( "object was " + ( 0 != mWeak.size() ? "not" : "" ) + "reclaimed" ); } to public static void main( String[] args ) { Map< Object, Object > mWeak = new WeakHashMap< Object, Object>(); try { mWeak.put( new Object(), new Object() ); } catch (Throwable t) {} flushMemory(); System.out.println( "object was " + ( 0 != mWeak.size() ? "not" : "" ) + "reclaimed" ); } then it runs fine, because the local variables used to hold the new Objects() are out of scope by the time the flushMemory is invoked. Similarly, my test program worked because the scope of the variables holding onto the objects was only within the loop. Do you want me to null out the local variables after an advice call on the off chance they are weak referenceable? I'd rather not, but if you can show me a real scenario where this is an issue, we can look to do that.
unsetting the target field which is currently set for something already released