Skip to content
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

Memory Leak of ChildContinuation introduced recently #1855

Closed
sunpeaksfivemile opened this issue Mar 10, 2020 · 1 comment
Closed

Memory Leak of ChildContinuation introduced recently #1855

sunpeaksfivemile opened this issue Mar 10, 2020 · 1 comment
Assignees
Labels

Comments

@sunpeaksfivemile
Copy link

sunpeaksfivemile commented Mar 10, 2020

I am trying to upgrade from 1.3.1 to 1.3.3 and one of my applications now has a memory leak. I can reproduce the leak in 1.3.3 and 1.3.4 and can roll back to 1.3.1 to resolve the leak.

Here is a simplified code snippet which reproduces the memory leak. I have a sample project setup with gradle if its useful:

    fun CoroutineScope.triggerChannel(triggersPerSecond: Int) = produce(capacity = 3) {
        val start = System.nanoTime()
        val interval = TimeUnit.SECONDS.toNanos(1) / triggersPerSecond
     
        var iteration = 0L
        while(true) {
            val now = System.nanoTime()
            val trigger = start + interval * iteration
     
            if (now - trigger > interval * 2) {
                iteration++
                continue
            }
     
            if (now <= trigger) {
                delay(TimeUnit.NANOSECONDS.toMillis(trigger - now))
                continue
            }
     
            iteration++
            send(Unit)
        }
    }
     
    fun main() = runBlocking {
        val channel = triggerChannel(300)
        repeat(10) {
            launch {
                for (value in channel) {
                    delay(10)
                }
            }
        }
    }

Over time 'kotlinx.coroutines.CancellableContinuationImpl' and 'kotlinx.coroutines.ChildContinuation' are leaked until memory pressure starts to have my application spending nearly 100% of time in GC. After running for a few hours we have 20m of these ChildContinuations and another 20m of the CancellableContinuationImpl.

Heres a screenshot from a few minutes of running this sample code:

Yourkit_screenshot

I found a forum post which seems to be a similar issue: https://discuss.kotlinlang.org/t/correct-layering-of-coroutine-scopes-to-prevent-leak/16493

@sunpeaksfivemile
Copy link
Author

sunpeaksfivemile commented Mar 10, 2020

Here is a more simplified code sample which reproduces the leak:

fun CoroutineScope.triggerChannel() = produce(capacity = 3) {
    while(true) {
        delay(1)
        send(Unit)
    }
}

fun main() = runBlocking {
    val channel = triggerChannel()
    for (value in channel) {
        delay(1)
    }
}

@qwwdfsad qwwdfsad self-assigned this Mar 11, 2020
@elizarov elizarov added the bug label Mar 11, 2020
qwwdfsad added a commit that referenced this issue Mar 11, 2020
…utineReusable within the same state machine and properly cleanup its child handle when its block completes

Fixes #1855
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants