Skip to content

Memory leak in coroutines 1.4.x #2564

@sunpeaksfivemile

Description

@sunpeaksfivemile

I've encountered a memory leak which I believe is caused by a race condition in the coroutine library.

I've simplified the leak down to the following repro code:

private suspend fun <T : Any> ReceiveChannel<T>.receiveBatch(size: Int): List<T> {
    return List(size) { receive() }
}

fun main() = runBlocking {
    class Thing(val counter: Long /* for debugging on the heap dump */)

    val channel1 = produce(capacity = 64) { // from the main thread
        (0 until Long.MAX_VALUE).forEach {
            send(Thing(it))
        }
    }

    launch(newSingleThreadContext("Channel2")) { // to a single thread (This thread is important! no leak if everything is single threaded)
        flow {
            while (true) {
                emit(channel1.receiveBatch(64))
            }
        }.collect { }
    }

    Unit
}

This sample code leaks Things very rapidly

Yourkit screenshot

I haven't dug too deep, but one interesting thing is if I inline the code for receive batch, there is no leak. Example:

fun main() = runBlocking {
    class Thing(val counter: Long /* for debugging on the heap dump */)

    val channel1 = produce(capacity = 64) { // from the main thread
        (0 until Long.MAX_VALUE).forEach {
            send(Thing(it))
        }
    }

    launch(newSingleThreadContext("Channel2")) { // to a single thread (This thread is important! no leak if everything is single threaded)
        flow {
            while (true) {
                emit(List(64) { channel1.receive() })
            }
        }.collect { }
    }

    Unit
}

Tested with kotlin 1.4.30 and coroutines-core 1.4.3

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions