You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose中LazyColumn嵌套LazyRow引发OOM内存溢出问题求助

问题原因
  1. 嵌套Lazy组件的独立缓存机制:LazyColumn每一行的LazyRow都是独立的Lazy列表实例,每个实例默认会缓存4个离屏子项、持有独立的滚动状态、测量数据和视图缓存,30行就存在30个独立的缓存池,内存占用会指数级升高。
  2. 低版本Compose的内存泄漏BUG:Compose 1.4以下版本存在已知的嵌套Lazy列表状态泄漏问题,当LazyColumn回收行条目时,内部LazyRow持有的滚动状态、子项缓存无法被GC正确回收,滚动次数越多内存堆积越严重。
  3. 未限制离屏缓存与测量优化:默认LazyRow没有固定高度,滚动过程中会频繁触发子项测量,测量数据会被持久化持有,进一步加剧内存占用。
  4. 数据类重复生成列表:TestDataBlock构造时每次都会生成21个新的TestDataItem实例,数据量大时会生成大量冗余对象。
解决办法

优先方案:替换为LazyVerticalGrid

网格类布局直接使用官方提供的LazyVerticalGrid实现,它是单一Lazy列表实例,统一管理所有子项的缓存与回收,完全避免嵌套Lazy组件的内存问题,示例修改如下:

@Composable
fun List(dataItems: List<TestDataBlock> = listOf()) {
    // 每行固定展示21个元素,可根据需求调整单元格规则
    LazyVerticalGrid(
        columns = GridCells.Fixed(21),
        modifier = Modifier.fillMaxSize()
    ) {
        items(dataItems.size * 21) { index ->
            val rowIndex = index / 21
            val columnIndex = index % 21
            val item = dataItems[rowIndex].testDataItems[columnIndex]
            Text(text = item.id, color = Color.Black, modifier = Modifier.width(100.dp))
        }
    }
}

TV端开发可以使用适配TV焦点的TvLazyVerticalGrid组件,效果一致。

兼容方案:必须保留嵌套滚动场景的优化

如果业务需要每一行独立横向滚动,无法替换为网格组件,可以通过以下方式优化:

  1. 升级Compose版本到1.4.0及以上,修复嵌套Lazy组件的内存泄漏底层BUG。
  2. 显式控制每个LazyRow的缓存数量,禁用不必要的离屏缓存:
@Composable
fun drawElement(rowItem: TestDataBlock) {
    Text(text = rowItem.id)
    // 绑定行id作为key,行回收时同步销毁状态,同时限制离屏缓存数量为0
    val lazyRowState = remember(key1 = rowItem.id) {
        LazyListState(beyondBoundsItemCount = 0)
    }
    LazyRow(
        state = lazyRowState,
        // 固定行高,避免重复测量
        modifier = Modifier.height(48.dp)
    ) {
        itemsIndexed(
            items = rowItem.testDataItems,
            key = { _, item -> item.id }
        ) { _, item ->
            Text(text = item.id, color = Color.Black, modifier = Modifier.width(100.dp))
        }
    }
}
  1. 优化数据实例,避免重复生成子项列表:将TestDataBlock中的testDataItems抽为全局缓存或者伴生对象存储,不需要每次构造TestDataBlock都生成新的列表。
@Immutable
data class TestDataBlock(
    val id: String,
    val data: Int,
) {
    companion object {
        // 公共缓存,所有实例共用一份子项列表
        private val sharedTestItems = (0..20).map { TestDataItem(it.toString()) }
    }
    val testDataItems: List<TestDataItem> = sharedTestItems
}

按照以上方案优化后,滚动20次的内存占用可以稳定在50MB以内,不会触发OOM问题。

内容的提问来源于stack exchange,提问作者Qalterra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 02:15:10