DEV Community

Abdullah Khan
Abdullah Khan

Posted on

Stop Leaking Coroutines: The Right Way to Make Classes Coroutine-Scoped

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)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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)