Java中的内存泄漏,但Kotlin(相同的代码库)没有...为什么?

Ely*_*lye 6 android memory-leaks kotlin

我在活动中有一段简单的代码......

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
        valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {

            }
        });
        valueAnimator.start();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果活动终止,将会有内存泄漏(Leak Canary证明).

但是,当我将此代码转换为相同的Kotlin代码(使用shift-alt-command-k)时,如下所示

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val valueAnimator = ValueAnimator.ofFloat(0.0f, 1.0f)
        valueAnimator.repeatCount = ValueAnimator.INFINITE
        valueAnimator.addUpdateListener { }
        valueAnimator.start()
    }
}
Run Code Online (Sandbox Code Playgroud)

内存泄漏不再发生.为什么?是因为匿名类对象被转换为Lambda?

aga*_*aga 6

这两个版本之间的区别非常简单.

Java版本AnimatorUpdateListener包含对外部类的隐式引用(在您的情况下为MainActivity).因此,如果动画在不再需要活动时继续运行,则侦听器会持续保持对活动的引用,从而防止对其进行垃圾回收.

科特林试图在这里变得更聪明.它看到你传递给它的lambda ValueAnimator不引用外部作用域中的任何对象(即MainActivity),因此它会创建一个单独的实例,AnimatorUpdateListener只要你[重新]启动动画,它就会被重用.并且此实例没有对外部作用域的任何隐式引用.

旁注:如果将对外部作用域中某个对象的引用添加到lambda,Kotlin将生成每次动画重新启动时创建更新侦听器的新实例的代码,这些实例将保持隐式引用MainActivity(为了访问您决定在lambda中使用的对象所必需的).

另一方面注意:我强烈建议阅读名为"Kotlin in Action"的书,因为它包含了很多有关Kotlin的有用信息,以及我对Kotlin编译器如何选择是否将隐式引用放入外部范围的解释SAM转换后创建的对象是否来自本书.


Ali*_*cia 2

1. 检查实际情况

\n\n

我想您会发现 \xe2\x80\x9cShow Kotlin Bytecode\xe2\x80\x9d 视图对于准确了解正在发生的情况非常有帮助。请参阅此处了解 InteliJ 快捷方式。\n (本来可以为您完成此操作,但如果没有更多应用程序的上下文,这很难)

\n\n

2. JVM 相似,但 Kotlin 显着差异

\n\n

但由于 Kotlin 与 Java 运行在相同的 JVM 上(因此使用与 Java 相同的垃圾收集器),因此您应该期待一个类似安全的运行时环境。话虽这么说,当涉及到 Lambda 和显式引用时,当它们被转换时。在不同的情况下它可能会有所不同:

\n\n

与 Java 一样,Kotlin 中发生的情况在不同情况下会有所不同。

\n\n
\n
    \n
  • 如果将 lambda 传递给内联函数并且\xe2\x80\x99t 标记为 noinline,那么整个事情就会消失,并且不会
    创建任何其他类或对象。
  • \n
  • 如果 lambda 没有\xe2\x80\x99t 捕获,那么它\xe2\x80\x99 将作为单例类发出,其实例被一次又一次地重用(一个类+一个\n 对象分配)。
  • \n
  • 如果 lambda 捕获,则每次使用 lambda 时都会创建一个新对象。
  • \n
\n
\n\n

来源: http: //openjdk.java.net/jeps/8158765

\n\n

3.总结和延伸阅读

\n\n

这个答案应该解释你所看到的,我自己无法更好地解释它: https: //stackoverflow.com/a/42272484/979052 \n不同的问题,我知道,但背后的理论是相同的 - 希望有所帮助

\n