【问题标题】:Creating anonymous object with inline function. Does it contain a leak of Enclosing Class?使用内联函数创建匿名对象。它是否包含封闭类的泄漏?
【发布时间】:2018-10-03 15:45:12
【问题描述】:

如您所知,java 中的每个匿名对象都包含对封闭类的隐藏引用。
但是随着 kotling,事情变得更加复杂。

Lambda 是匿名类的另一种表示,但在 kotlin 中它的编译并不简单,因为如果 lambda 没有显式捕获封闭类的引用,那么它将像嵌套而不是内部类(匿名类)那样编译并且是安全的从泄漏。

但是内联函数呢?考虑下面的代码

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()
}

据我所知,内联函数会像未包装的代码一样编译到 A 类,所以问题是开放的。

匿名object: Reference 是否包含指向封闭类A 的链接,这可能导致内存泄漏?

PS:我已阅读 this article,但它不包含我的案例的答案

【问题讨论】:

    标签: memory-leaks kotlin


    【解决方案1】:

    如果您考虑一下,withReference 函数无法引用它被内联到的外部范围,因此它没有理由包含对调用它的范围的引用。就此而言,您甚至不知道它在哪个类中被调用,或者它是否在一个类中被调用。

    对于这种特殊情况,这里是withReference函数的反编译和简化字节码:

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

    在它被内联的地方,当然没有调用这个函数,这个函数只用于 Java 互操作。 Kotlin 调用站点都会生成自己的类来表示此对象,具体取决于您传递给action 参数的代码。对于test 函数的调用,会生成一个类似的类:

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

    这就是 test 方法中实例化的内容:

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

    【讨论】:

      【解决方案2】:

      我用的是IntelliJ的反编译器,没有对外A的引用

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

      如果 lambda 像这样引用外部类 A 中的变量:

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

      然后反编译器显示对A对象的引用:

      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() {
         }
      }
      

      【讨论】:

        猜你喜欢
        • 2013-03-22
        • 2015-12-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多