使用coroutineScope时内存泄漏

问题描述 投票:0回答:1

我遇到过一个应用程序,当异步调用 api 的方法之一使用协程作用域时,该应用程序似乎出现了内存泄漏。当我忽略它时,问题就消失了

以下 kotlin 代码重现了我在应用程序中看到的内存泄漏:

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.runBlocking

class MyApp {

    suspend fun run() {
        myReport()
        // while this is sleeping trigger a heapdump with visual vm
        Thread.sleep(1000000000)
    }

    suspend fun myReport(): List<String> = runGaqlRequest().map { "some keyword" }

    suspend fun runGaqlRequest(): List<String> = io {
        listOf("A".repeat(300_000_000))  // very large string using 80% of a 1024mb heap
    }

    suspend inline fun <R> io(crossinline body: suspend () -> R): R =
        coroutineScope { async(Dispatchers.IO) { body() }.await() }
}

fun main() {
    val app = MyApp()

    runBlocking {
        app.run()
    }
}

runGaqlRequest
方法模拟从API端点读取响应。这里它将返回一个包含单个元素的列表。一个巨大的 300.00.000 字符串

myReport
方法将此列表中的每个元素转换为字符串“some keywords”,因此该函数的输出是一个包含单个元素“some keywords”元素的列表

我从

myReport
方法调用
run
,然后它会休眠很长一段时间

此时,我希望巨大的字符串有资格进行垃圾收集,但是当我使用 VisualVM 创建进程的堆转储时,我看到它仍然存在并且无法被垃圾收集

当我将 runGaqlRequest 方法更改为此时

    suspend fun runGaqlRequest(): List<String>  {
        return listOf("A".repeat(300_000_000))  // very large string using 80% of a 1024mb heap
    }

因此它不使用 coroutineScope,问题就消失了。

kotlin out-of-memory
1个回答
0
投票

这可能在于您拥有

suspend
功能。没有它还会有问题吗?

    fun run() {
        myReport()
        // while this is sleeping trigger a heapdump with visual vm
        Thread.sleep(1000000000)
    }

    ...

    inline fun <R> io(crossinline body: suspend () -> R): R =
        runBlocking { coroutineScope { async(Dispatchers.IO) { body() }.await() } }

(虽然我不确定它是否有帮助,但其背后的想法是,由于

suspend
关键字,
run
方法会等待
coroutineScope
,因此可能会因某种原因阻止垃圾收集。)

© www.soinside.com 2019 - 2024. All rights reserved.