Flutter'Compute'内存泄漏 - 如何淘汰计算实例使用的堆变量?

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

我正在尝试在我的移动应用程序中使用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消失。我已经考虑过了。我可以在代码中添加人工延迟,以便在映射方法中引入异步拆分,但这不是我想要做的 - 我需要尽可能快地返回数据,这就是为什么使用计算是理想的。

即使在“流正在关闭”之后打印,并且流被关闭,并且父对象从层次结构中退出并被收集,与计算方法相关联的任何内存都不会退役。

如何使计算实例正确退出内存?我有什么问题吗?

dart flutter
1个回答
0
投票

使用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();
}

请注意顶级功能,希望对此有所帮助。

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