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

Cookie-Einstellungen

Diese Website verwendet Cookies, um Inhalte und Anzeigen zu personalisieren, Funktionen für soziale Medien anbieten zu können und Zugriffe auf die Website zu analysieren. Zudem werden Informationen zu Ihrer Verwendung der Website an Partner für soziale Medien, Werbung und Analysen weitergegeben. Die Partner führen diese Informationen möglicherweise mit weiteren Daten zusammen, die Sie ihnen bereitgestellt haben oder die sie im Rahmen Ihrer Nutzung der Dienste gesammelt haben.

Weitere Informationen finden Sie in unserer Datenschutzerklärung. Dort können Sie nachträglich auch Ihre Cookie-Einstellungen ändern.

contact icon

Kontakt aufnehmen