Kotlin/Java Memory Leaks - jambit ToiletPaper

Kotlin / Java Memory Leaks

Problem: Memory Leaks bei Verwendung von Kotlin und Java

Bei der Verwendung von Kotlin und Java kann es im Zusammenspiel mit Java Lambdas zu Memory Leaks kommen. Beispiel: In Kotlin wird ein Listener erzeugt, der dann bei einer Java-Klasse registriert und vermeintlich wieder deregistriert wird:

public class Calculator {
    interface Listener {
        void onResult(int result);
    }
    Set<Listener> listeners = new HashSet<>();
    void addListener(Listener listener) {
        listeners.add(listener);
    }
    void removeListener(Listener listener) {
        listeners.remove(listener);
    }
    ...
}
fun main() {
    val c = Calculator()

    val listener = { sum: Int ->
        println("sum: $sum")
    }

    c.addListener(listener)
    c.removeListener(listener)
    c.sum(10, 20)
}

Beim Ausführen der Kotlin main-Funktion würde man eigentlich keine Ausgabe erwarten, allerdings funktioniert der Aufruf von c.removeListener nicht wie erwartet. Ein Klick in Android Studio auf "Tools → Kotlin → Show Kotlin Bytecode" gefolgt von einem Klick auf "Decompile" zeigt die Ursache im generierten Java-Code:

Object var10001 = listener;
if (listener != null) {
   var10001 = new MainKt$sam$test_Calculator_Listener$0(listener);
}
c.addListener((Listener)var10001);
var10001 = listener;
if (listener != null) {
   var10001 = new MainKt$sam$test_Calculator_Listener$0(listener);
}
c.removeListener((Listener)var10001);

listener wird jeweils nochmal in MainKt$sam$test_Calculator_Listener$0(listener) gewrapped und erst dann dem addListener bzw. removeListener übergeben, was zu dem Zeitpunkt dann zwei unterschiedliche Java-Objekte sind.

Lösung: Die Deklaration von listener abwandeln

Die Deklaration von listener kann leicht abgewandelt werden, damit entfällt dann der nötige generierte Wrapper:

val listener = Calculator.Listener {
  sum: Int -> println("sum: $sum")
}
c.addListener(listener)
c.removeListener(listener)
Listener listener = (Listener)null.INSTANCE;
c.addListener(listener);
c.removeListener(listener);
Kotlin / Java Memory Leaks vermeiden - jambit ToiletPaper

Weiterführende Aspekte

---

Autor: Marco Pfattner / Senior Software Architect/ Business Division Automotive Bavaria

Wir verwenden Cookies, um unsere Webseite für Sie zu optimieren. Mit dem Besuch unserer Webseite erklären Sie sich damit einverstanden. // Our website is using cookies to improve your experience. By continuing to browse the site, you are agreeing to our use of cookies.

Weitere Informationen finden Sie in unserer Datenschutzerklärung. // For more information, please refer to our privacy policy.