One Off the Slack: When Do Composition Contents Get GC'd?

Ryan Kay asked:

I have a question about memory management for classes which are instantiated within compose functions. I have a KMM project, and for the desktop version, I create a few pure Kotlin classes within my top level composable. I use a remember delegate which contains a enum class denoting each screen of the application (four screens total). When I want to navigate to a new screen in the desktop version, I just switch change that remembered enum and the recomposition occurs.

So the thing I’m wondering is how to make sure that the classes (basically a Presenter, ViewModel, and Container class for a given screen) are being GCed properly after recomposition occurs. It’s worth noting that they are instantiated within a composable that gets recomposed. Is there a general rule about what happens to such classes? My guess is that they are held on to via the slotTable, but I don’t know for sure.

Ryan also supplied an example:

fun main() = application {
//...
    var windowState by remember {
        mutableStateOf(WindowState.VIEW_DAY)
    }
//..
    Window(onCloseRequest = ::exitApplication, title = "Todo", resizable = false) {

        SamsaraTheme {
            when (windowState) {
                WindowState.VIEW_DAY -> {
                    val vm = DayViewModel()
                    val container = buildDayFeature(
                        { newState: WindowState, arg: Int ->
                            windowState = newState
                            windowArgs = arg
                        },
                        vm,
                        storageService
                    )

                    DayViewScreen(
                        container.logic::onViewEvent,
                        vm
                    ) //...
                }}}}}


fun buildDayFeature(
    stateHandler: (WindowState, Int) -> Unit,
    vm: DayViewModel,
    storageService: StorageService
): DayViewContainer =
    DayViewContainer(stateHandler).start(storageService, vm)

Google’s Adam Powell replied:

If you remember {} something it will be hard referenced by the composition. Any lambda captures you create that Compose might need to re-run to recompose will also be hard referenced by the composition. These references are dropped when the @Composable caller leaves the composition, or when the composition itself is disposed.

So, in Ryan’s example, windowState will be retained across recompositions. But, as Adam then pointed out, this is not the case for everything in that sample:

Your val vm = DayViewModel() will create a new instance of DayViewModel each time the SamsaraTheme trailing lambda function recomposes and windowState == WindowState.VIEW_DAY. Generally when someone writes something called ViewModel this is not what they intend. 🙂 You might want to wrap a remember {} around that (val vm = remember { DayViewModel() }) so that it keeps the same instance across recompositions.


Read the original thread in the kotlinlang Slack workspace. Not a member? Join that Slack workspace here!