我正在尝试在我的移动应用程序中使用compute实例,以便在将一堆下载的JSON反序列化为对象时减少jank。
当我使用compute实现反序列化方法时,堆保持传递的JSON和返回的反序列化对象(在列表中)INDEFINITELY。 GC正常触发,但不会从堆中删除对象,即使方法已关闭且父/调用对象已停用。因此,当使用DevTools内存分析器时,它会显示失控的内存消耗 - 堆只会不断变大。
正常内存配置文件 - 直接调用反序列化方法时内存使用率徘徊在45MB左右(但它会导致应用程序中的jank)
失控内存配置文件 - 内存使用率线性增加,并且在通过计算调用反序列化方法时永远不会退出(但它不会在应用程序中导致jank)
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更改为异步方法对上面的任何分析都没有影响。将它变为异步也不会导致jank消失。我已经考虑过了。我可以在代码中添加人工延迟,以便在映射方法中引入异步拆分,但这不是我想要做的 - 我需要尽可能快地返回数据,这就是为什么使用计算是理想的。
即使在“流正在关闭”之后打印,并且流被关闭,并且父对象从层次结构中退出并被收集,与计算方法相关联的任何内存都不会退役。
如何使计算实例正确退出内存?我有什么问题吗?
使用Isolates时,回调参数必须是顶级函数。
回调参数必须是顶级函数,而不是闭包或类的实例或静态方法。
您可以尝试以下方式:
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();
}
请注意顶级功能,希望对此有所帮助。