Stream处理百万级对象内存占用过高问题求助
问题描述
我正在生成某地理区域内的大量坐标(由3个数字组成)。原本以为Stream比List更高效,结果应用内存被快速占满,Observatory截图也证明了这一点。我需要的是一种能逐个存入、读取后就从结构中移除的结构,按理解Stream应该是这样的——添加新值时旧值会被移除,但实际情况是Stream(或其读取端)一直在膨胀,而我只是对返回的Stream调用了.length方法。
相关代码如下:
static Stream<Coords<num>> _generateTilesComputer( DownloadableRegion region, ) async* { List<List<double>> serialiseOutline(l) => (l as List) .cast<LatLng>() .map((e) => [e.latitude, e.latitude]) .toList(); final port = ReceivePort(); final tilesCalc = await Isolate.spawn( region.type == RegionType.rectangle ? rectangleTiles : region.type == RegionType.circle ? circleTiles : lineTiles, { 'port': port.sendPort, 'rectOutline': region.type != RegionType.rectangle ? null : serialiseOutline(region.points), 'circleOutline': region.type != RegionType.circle ? null : serialiseOutline(region.points), 'lineOutline': region.type != RegionType.line ? null : (region.points as List<List<LatLng>>) .chunked(4) .map((e) => e.map(serialiseOutline)), 'minZoom': region.minZoom, 'maxZoom': region.maxZoom, 'crs': region.crs, 'tileSize': region.options.tileSize, }, ); await for (final Coords<num>? coord in port .skip(region.start) .take(((region.end ?? double.maxFinite) - region.start).toInt()) .cast()) { if (coord == null) { port.close(); tilesCalc.kill(); return; } yield coord; } } }
问题根源
调用Stream.length()时,Dart会将流中所有事件缓存到内部列表,直到流完全结束,以此来统计总数。这直接导致所有生成的坐标都被保留在内存中,完全违背了Stream流式处理、边产生边消费的设计初衷,内存自然会快速暴涨。
解决方案
1. 替换length()为手动计数
放弃调用Stream.length(),改为遍历流时手动维护计数器:
// 示例:手动统计流中事件数量 int count = 0; await for (final coord in _generateTilesComputer(region)) { // 这里可以同时处理每个coord(比如存入数据库/文件) count++; } print('总坐标数:$count');
这种方式不会缓存任何事件,每处理一个就丢弃一个,内存占用始终保持在低水平。
2. 优化流与Isolate的资源管理
- 修正
serialiseOutline的逻辑错误:原代码中[e.latitude, e.latitude]应该是[e.latitude, e.longitude],避免坐标数据错误; - 用
Isolate.exit()替代kill():kill()是强制终止,可能导致资源泄漏,exit()会让Isolate正常清理资源后退出; - 确保ReceivePort在流结束后及时关闭,避免端口资源残留。
修改后的_generateTilesComputer关键部分:
// 修正serialiseOutline方法 List<List<double>> serialiseOutline(l) => (l as List) .cast<LatLng>() .map((e) => [e.latitude, e.longitude]) // 修正为longitude .toList(); // 终止Isolate的逻辑修改 if (coord == null) { port.close(); await Isolate.exit(tilesCalc); // 优雅退出 return; }
内容的提问来源于stack exchange,提问作者JaffaKetchup
相关产品推荐
相关产品推荐

