在将一堆下载的JSON反序列化为对象时,我试图在移动应用程序中使用compute实例来减少jank。
当我使用compute实现反序列化方法时,堆会无限期地同时保留传递的JSON 和返回的反序列化对象(在列表中)。即使该方法已关闭并且父/调用对象已退出,GC仍会正常触发但不会从堆中删除对象。因此,在使用DevTools内存分析器时,它显示出内存消耗失控-堆不断增大。
正常内存配置文件-直接调用反序列化方法时,内存使用量徘徊在45MB左右(但这会导致应用中出现垃圾)
失控的内存配置文件-内存使用量呈线性增加,并且在通过计算调用反序列化方法(但不会在应用程序中造成垃圾)时永远不会停用
static Stream<EventCommitInfoModel> getEventsAfterDate(DateTime date) async* {
// variable defs for scope reuse
while (count < maxCount && retryCount > 0) {
try {
json = await http.read(url);
// currentEvents = await compute(EventModel.fromJsonArray, json);
currentEvents = EventModel.fromJsonArray(json);
db = await AppStateModel.database;
await db.upsertEventModels(currentEvents);
yield new InfoModel(maxCount, currentEvents.length);
}
catch (ex) {
// try again or close
}
}
print("stream is closing.");
}
在上面的代码中,相关行以“ currentEvents =”开头。正常的内存行为可以通过以下方式看到:
currentEvents = EventModel.fromJsonArray(json);
并显示出失控的内存行为:
currentEvents = await compute(EventModel.fromJsonArray, json);
请注意,将EventModel.fromJsonArray更改为异步方法对以上任何概要分析都具有 NO 的影响。将其更改为异步也不会导致垃圾消失。我已经考虑过了。我可以在代码中添加人为延迟,以便在映射方法中引入异步拆分,但这不是我要在这里做的-我需要数据尽快返回,这就是为什么使用计算机是理想的选择。
即使在打印“关闭流”并且关闭流,并且从层次结构撤消父对象并收集父对象之后,也永远不会撤消与计算方法关联的任何内存。
如何使计算实例正确地退出内存?我在这里做错什么了吗?
答案 0 :(得分:2)
没有更多代码,似乎正在发生的事情是您正在创建的流从未真正关闭过。因此,流保持打开状态,并因此钩住了GC无法删除的json对象。
您在问题陈述中暗示流已关闭:
Even after "stream is closing" is printed, and the stream is closed, and ...
...但是,有关流将关闭的打印语句不会关闭流。仅仅到达代码块的末尾也不会关闭流(尤其是对于广播流),至少不会关闭。
根据Dart文档,他们也不建议直接对流进行操作; https://dart.dev/articles/libraries/creating-streams#final-hints也正是出于这个原因。他们建议使用流控制器。
答案 1 :(得分:0)
使用隔离时,回调参数必须是顶级函数。
回调参数必须是顶级函数,而不是闭包或 类的实例或静态方法。
您可以尝试以下操作:
class EventsRepo {
static const URL = 'https://some-json.com/events';
@override
Future<List<Event>> fetchEvents(http.Client client) async {
final response = await client.get(URL);
return compute(parseJson, response.body);
}
}
// Top level function
List<Event> parseJson(String responseBody) {
final parsed = json.decode(responseBody);
return parsed.map<Event>((json) => EventModel.fromJson(json)).toList();
}
请注意顶层功能,希望有帮助。
答案 2 :(得分:0)
不确定Flutter的旧版本,但是Channel master, 1.21.0-6.0.pre.41
的当前版本对compute
函数没有任何问题。
我正在项目中使用它,似乎内存没有增长。
要点compute
是杀死引擎盖下的孤立者。隔离在被杀死时释放它的记忆。
/// The dart:io implementation of [isolate.compute].
Future<R> compute<Q, R>(isolates.ComputeCallback<Q, R> callback, Q message, { String? debugLabel }) async {
... other code ...
resultPort.close();
errorPort.close();
isolate.kill();
Timeline.finishSync();
return result.future;
}
如果直接使用Isolate.spawn,请确保保留实例并在工作完成后手动将其杀死。我也正在使用这种方法。
release() {
_subscription.cancel();
_controller.close();
_isolate.kill(
priority: Isolate.immediate,
);
}
答案 3 :(得分:0)
我在 flutter 2.2.3 上看到了同样的问题
我作为参数传递给计算的所有 data
都保存在内存中 - await compute(exampleGlobalFunction, data)
。
我在 Flutter DevTools
/ Memory
选项卡中检查过,数据实例从未发布。如果我直接调用函数(没有计算),那么数据会被正确释放。
编辑:我想我找到了解决方案 - 创建了一个 github 问题 https://github.com/flutter/flutter/issues/86470