Do inner classes cause a memory leak?

Reinier Zwitserloot reinier at zwitserloot.com
Fri Jul 16 12:18:03 PDT 2010


Correct; the way the JLS currently specifies how anonymous inner classes
work means they can NOT be static, and that a reference to the outer
instance is passed into the inner instance even if it isn't actually
necessary. The gc in turn cannot use hotspot tech to realize a certain inner
field is blocking a gc but isn't actually useful for anything, as one can
always get this value back out using for example reflection.

However, the draft plan for translating lambdas to bytecode uses an entirely
different mechanism, and your problem will not occur if this draft plan
doesn't change too drastically. The full plan can be read here:

http://mail.openjdk.java.net/pipermail/lambda-dev/2010-May/001355.html

Check chapter 5 named 'Type 1 lambdas - "stateless" functions'.

 --Reinier Zwitserloot



On Fri, Jul 16, 2010 at 7:28 PM, Howard Lewis Ship <hlship at gmail.com> wrote:

> I use a huge number of inner classes for closure-like operations in
> Tapestry.  One thing I've noticed (often while using the debugger) is
> that non-static inner classes appear to keep a reference to the
> containing object, even when not absolutely necessary.
>
> Consider the following:
>
> package com.example;
>
> public class LeakExample
> {
>    public Runnable create()
>    {
>        return new Runnable()
>        {
>            public void run()
>            {
>                System.out.println("Inner class.");
>            }
>        };
>    }
> }
>
>
> Usine JDK 1.6 on my Mac:
>
> $ jad -a -p  target/test-classes/com/example/LeakExample*.class
> // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
> // Jad home page: http://www.kpdus.com/jad.html
> // Decompiler options: packimports(3) annotate
> // Source File Name:   LeakExample.java
>
> package com.example;
>
> import java.io.PrintStream;
>
> public class LeakExample
> {
>
>    public LeakExample()
>    {
>    //    0    0:aload_0
>    //    1    1:invokespecial   #1   <Method void Object()>
>    //    2    4:return
>    }
>
>    public Runnable create()
>    {
>        return new Runnable() {
>
>            public void run()
>            {
>                System.out.println("Inner class.");
>            //    0    0:getstatic       #3   <Field PrintStream System.out>
>            //    1    3:ldc1            #4   <String "Inner class.">
>            //    2    5:invokevirtual   #5   <Method void
> PrintStream.println(String)>
>            //    3    8:return
>            }
>
>            final LeakExample this$0;
>
>
>            {
>                this$0 = LeakExample.this;
>            //    0    0:aload_0
>            //    1    1:aload_1
>            //    2    2:putfield        #1   <Field LeakExample this$0>
>                super();
>            //    3    5:aload_0
>            //    4    6:invokespecial   #2   <Method void Object()>
>            //    5    9:return
>            }
>        }
> ;
>    //    0    0:new             #2   <Class LeakExample$1>
>    //    1    3:dup
>    //    2    4:aload_0
>    //    3    5:invokespecial   #3   <Method void
> LeakExample$1(LeakExample)>
>    //    4    8:areturn
>    }
> }
> ~/work/t5-project/tapestry-ioc
> $
>
>
>
>
> ~/work/t5-project/tapestry-ioc
> $ javac -version
> javac 1.6.0_20
>
>
> So looking at this, or the raw bytecode, it seems a non-static inner
> class always includes a reference to the containing class, even when
> (as in this example) the inner class has no dependencies on (private)
> members of the enclosing class. Perhaps I'm misreading the dissembled
> bytecode, but I think not.
>
> It seems to me that basic escape analysis should make it possible to
> avoid this case, both for inner classes, and for the lambda syntax
> being proposed. In fact, as long as the inner class only refers to
> final instance variables (that could be passed to the constructed
> inner class via its constructor) and not to methods of the outer
> class, it should always be possible to avoid the leaked this
> reference.
>
> This is important to me; Tapestry often follows a pattern wherein
> large builder objects (full of mutable state) execute to produce final
> runtime objects. Often inner classes (poor man's closures) are part of
> this process. I want the builder objects to be GC'ed once the runtime
> objects are created. It is all too easy for an inner class to hold a
> reference to a builder object and prevent it from being GC'ed.
>
> Currently, I frequently resort to a static method to construct inner
> classes so as to ensure that this does not escape (and with current
> inner class syntax, that's often preferable from a code readability
> point of view).  Further, when an inner class is used inline (with the
> leaked this), my code only does so to gain access to a final field
> (typically an injected dependency).
>
> --
> Howard M. Lewis Ship
>
> Creator of Apache Tapestry
>
> The source for Tapestry training, mentoring and support. Contact me to
> learn how I can get you up and productive in Tapestry fast!
>
> (971) 678-5210
> http://howardlewisship.com
>
>


More information about the lambda-dev mailing list