Beloo
Beloo

Reputation: 9925

Creating anonymous object with inline function. Does it contain a leak of Enclosing Class?

As you know each anonymous object in java contains hidden reference to enclosing class.
But with kotling things get more complicated.

Lambda is another representation of anonymous class, but in kotlin it compiles not straightforward, because if lambda didn't capture a reference of enclosing class explicitely than it would be compiled like nested, not inner class (anonymous class) and is safe from the leak.

But what about inline functions. Consider the code below

class A {
    fun test(){
        val it = withReference {
            //todo make sth
        }
    }

}

inline fun withReference(crossinline action: () -> Unit) = object: Reference {
    override fun method1() {
        action()
    }
    override fun method2() {
    }
}

interface Reference {
    fun method1()
    fun method2()
}

As i know inline function would be compiled like non-wrapped code to the A class, so the question is open.

Does the anonymous object: Reference contain a link to enclosing class A, which could lead to a memory leak?

PS: i have read this article, but it doesn't contain an answer to my case

Upvotes: 5

Views: 1078

Answers (2)

Rene
Rene

Reputation: 6248

I used the decompiler of IntelliJ and there is no reference to the outer A

public final class A$test$$inlined$withReference$1 implements Reference {
    public void method1() {
    }

    public void method2() {
    }
}

If the lambda references a variable from the outer class A like this:

class A {
    val valFromA = 10;
    fun test(){
        val it = withReference {
            println("use $valFromA")
        }
    }
}

Then the decompiler shows the reference to the A object:

public final class A$test$$inlined$withReference$1 implements Reference {
   // $FF: synthetic field
   final A this$0;

   public A$test$$inlined$withReference$1(A var1) {
      this.this$0 = var1;
   }

   public void method1() {
      String var1 = "use " + this.this$0.getValFromA();
      System.out.println(var1);
   }

   public void method2() {
   }
}

Upvotes: 2

zsmb13
zsmb13

Reputation: 89578

If you think about it, the withReference function has no way of referring to the outer scope that it gets inlined into, therefore it has no reason to contain a reference to the scope that it's called from. You don't even know what class it's being called in, or if it's even called inside a class, for that matter.

For this specific case, here's the decompiled and simplified bytecode of the withReference function:

public static Reference withReference(final Function0 action) {
    return new Reference() {
        public void method1() {
            action.invoke();
        }

        public void method2() {
        }
    };
}

At the places where it gets inlined, there's of course no call to this function, this one is for Java interop only. Kotlin call sites all get their own class generated to represent this object depending on what code you pass into the action parameter. For your call of the test function, a class like is generated:

public final class A$test$$inlined$withReference$1 implements Reference {
    public void method1() {
        //todo make sth
    }
    public void method2() {
    }
}

And this is what's instantiated in the test method:

public final class A {
    public final void test() {
        Reference it = new A$test$$inlined$withReference$1();
    }
}

Upvotes: 2

Related Questions