If you create a CoroutineScope inside a class and never cancel it…
you’ve already built a silent leak.
It won’t crash.
It won’t throw.
It won’t warn you.
It will just quietly keep running long after the object that started it is gone.
Most developers don’t notice this — because everything “works”.
Until it doesn’t.
The Common Pattern That Causes It
You’ve probably written something like this:
class SimpleTask {
val scope = CoroutineScope(Dispatchers.Default)
fun runTask() = scope.launch {
for (i in 0..10) {
println(i)
}
}
}
Looks fine, right?
The object owns the scope.
You can even add close() and cancel it manually.
But here’s the real problem:
Cancellation is optional.
If the caller forgets to call close() —
the coroutine keeps running.
Now you’ve created what I call a runaway CoroutineScope.
Why Garbage Collection Won’t Save You
Many developers assume:
“When the object is garbage collected, everything inside it disappears.”
Not true.
If your coroutine is still running, it strongly references the object.
The object cannot be garbage collected.
You’ve accidentally created a reference cycle.
This is where most coroutine ownership discussions stop.
But there is a proper architectural solution.
It requires:
Separating scope from owner
Using WeakReference correctly
Designing strict ownership boundaries
Supporting both automatic and manual cancellation
And it works even outside Android.
I wrote a full deep dive explaining the architecture, the failure cases, and the correct implementation pattern here:
👉 Read the full article:
Stop Leaking Coroutines: The Right Way to Make Classes Coroutine-Scoped
Top comments (0)