Reachability determines what remains in memory
Garbage collection reclaims objects that are no longer reachable; it cannot infer that a reachable object is no longer useful to your application. A long-lived collection or callback can therefore create a memory leak without manual allocation mistakes.
class ListenerRegistry {
private val listeners = mutableListOf<() -> Unit>()
fun add(listener: () -> Unit) { listeners.add(listener) }
fun remove(listener: () -> Unit) { listeners.remove(listener) }
fun notifyAllListeners() { listeners.toList().forEach { it() } }
}
A listener may capture a screen and retain it until removed. This example is not thread-safe; mutation during callbacks is handled only by iterating a snapshot. Resource cleanup still needs explicit ownership because GC does not reliably release files or subscriptions at the moment you want.
Kotlin/JVM and Kotlin/Native have different runtime implementations. Do not carry obsolete assumptions about Native's historical memory model into current projects.
Exercise
Register and unregister the same callback instance. Use a profiler to examine a deliberately retained object, then remove the retaining reference.
Check: forcing GC is not a fix for an object still reachable through your registry.