-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Release coroutine block after lazy start #2111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
When starting a coroutine lazily by using CoroutineStart.LAZY, it creates a Continuation that would launch the coroutine block, which is stored in the returned Job or Deferred to be invoked lazily. However, this reference is never cleared, even after the coroutine ends. This prevents the coroutine block and it's enclosing class instance from being garbage collected if the Job or Deferred is retained in another class, or while child coroutines are running (as they also hold a reference to the Job). Fixed by clearing the reference to the Continuation after invoking it.
You should consider the I already fixed this issue some time ago 🙄 #767 |
Thank you for your effort. Personal opinion: an exclamation mark at the end of the message does not add clarity, it is just an extra byte (or 2-bytes). Short messages do not need punctuation. |
It needs a use-case (a practical example where this code matters) and a test (we have |
Hi, @elizarov,
I consider to wipe references a best-practice and this PR is able to detect double start issue (with a bit of luck). Coroutine life-time is undefined and its memory requirement isn't constrained. |
I don't have a specific use-case at the moment that would be significantly affected by this. However, as @fvasco mentioned, it's good to release objects once they're no longer needed. Also, the memory implications of I tried to write some tests using @Test
fun testBlockIsReachableInCoroutineStartingContinuation() = runTest {
val block = suspend {}
val continuation = block.createCoroutineUnintercepted(object : Continuation<Unit> {
override val context = EmptyCoroutineContext
override fun resumeWith(result: Result<Unit>) {}
})
FieldWalker.assertReachableCount(1, continuation) { it === block }
} This fails with |
@fvasco KT-16222 is indeed a bummer and we are aiming to fix it soon. Does this cleanup actually solves application-specific memory leaks or are you concerned that the absence of such cleanup may trigger memory-leaks in the future? |
Thank you @qwwdfsad, Regarding this one, we not profiled our server recently, so it is only a threat.
I don't agree with this statement: the code always triggers a memory leak, it is not possible to avoid it. I don't think that it is productive to discuss how big it can be. |
Thanks! Superseded with #2511 |
When starting a coroutine lazily by using
CoroutineStart.LAZY
, it creates aContinuation
that would launch the coroutine block, which is stored in the returnedJob
orDeferred
to be invoked lazily. However, this reference is never cleared, even after the coroutine ends.This prevents the coroutine block and it's enclosing class instance from being garbage collected if the
Job
orDeferred
is retained in another class, or while child coroutines are running (as they also hold a reference to theJob
).Fixed by clearing the reference to the
Continuation
after invoking it.